From 6836b029f71b3cc3cda1bd240726baa09e89fb28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Almeida?= Date: Tue, 30 Apr 2024 10:11:56 +0100 Subject: [PATCH 1/7] ExtractThinker first code. Basic tests working --- .flake8 | 2 + .github/workflows/workflow.yml | 31 +++ .pre-commit-config.yaml | 23 ++ .ruff.toml | 62 +++++ .vscode/launch.json | 15 ++ extract_thinker/__init__.py | 0 extract_thinker/app.py | 51 ++++ .../azure_form_recognizer_loader.py | 11 + .../document_loader/doctr_loader.py | 11 + .../document_loader/document_loader.py | 67 +++++ .../document_loader_tesseract.py | 49 ++++ .../document_loader/llm_interceptor.py | 7 + .../document_loader/loader_interceptor.py | 7 + .../document_loader/text_extract_loader.py | 11 + extract_thinker/extractor.py | 243 ++++++++++++++++++ extract_thinker/image_splitter.py | 65 +++++ extract_thinker/llm.py | 16 ++ extract_thinker/models.py | 37 +++ extract_thinker/splitter.py | 43 ++++ extract_thinker/text_splitter.py | 38 +++ extract_thinker/utils.py | 15 ++ .../Antropic}/AnthropicsApiRequest.py | 0 .../Antropic}/AnthropicsApiResponse.py | 0 .../Antropic}/AnthropicsApiService.py | 0 .../Antropic}/Content.py | 0 .../Antropic}/ErrorDetail.py | 0 .../Antropic}/Message.py | 0 {Antropic => medium_posts/Antropic}/Usage.py | 0 .../Classification}/Cascade.py | 0 .../Classification}/Classification.py | 0 .../Classification}/Eval.py | 0 .../Classification}/LogProbEval.py | 0 .../Classification}/Model35.py | 0 .../Classification}/Model4.py | 0 .../Classification}/ModelDecorator.py | 0 .../CustomException.py | 0 Dockerfile => medium_posts/Dockerfile | 0 Payload.py => medium_posts/Payload.py | 0 config.py => medium_posts/config.py | 0 {docTR => medium_posts/docTR}/Dockerfile | 0 {docTR => medium_posts/docTR}/README.md | 0 {docTR => medium_posts/docTR}/app.py | 0 main.py => medium_posts/main.py | 0 medium_posts/requirements.txt | Bin 0 -> 338 bytes utils.py => medium_posts/utils.py | 4 +- pyproject.toml | 29 +++ requirements.txt | Bin 338 -> 85 bytes tests/__init__.py | 0 tests/document_loader_tesseract.py | 38 +++ tests/extractor.py | 31 +++ tests/models/invoice.py | 6 + tests/test_document_loader.py | 0 tests/test_images/invoice.png | Bin 0 -> 47455 bytes 53 files changed, 911 insertions(+), 1 deletion(-) create mode 100644 .flake8 create mode 100644 .github/workflows/workflow.yml create mode 100644 .pre-commit-config.yaml create mode 100644 .ruff.toml create mode 100644 .vscode/launch.json create mode 100644 extract_thinker/__init__.py create mode 100644 extract_thinker/app.py create mode 100644 extract_thinker/document_loader/azure_form_recognizer_loader.py create mode 100644 extract_thinker/document_loader/doctr_loader.py create mode 100644 extract_thinker/document_loader/document_loader.py create mode 100644 extract_thinker/document_loader/document_loader_tesseract.py create mode 100644 extract_thinker/document_loader/llm_interceptor.py create mode 100644 extract_thinker/document_loader/loader_interceptor.py create mode 100644 extract_thinker/document_loader/text_extract_loader.py create mode 100644 extract_thinker/extractor.py create mode 100644 extract_thinker/image_splitter.py create mode 100644 extract_thinker/llm.py create mode 100644 extract_thinker/models.py create mode 100644 extract_thinker/splitter.py create mode 100644 extract_thinker/text_splitter.py create mode 100644 extract_thinker/utils.py rename {Antropic => medium_posts/Antropic}/AnthropicsApiRequest.py (100%) rename {Antropic => medium_posts/Antropic}/AnthropicsApiResponse.py (100%) rename {Antropic => medium_posts/Antropic}/AnthropicsApiService.py (100%) rename {Antropic => medium_posts/Antropic}/Content.py (100%) rename {Antropic => medium_posts/Antropic}/ErrorDetail.py (100%) rename {Antropic => medium_posts/Antropic}/Message.py (100%) rename {Antropic => medium_posts/Antropic}/Usage.py (100%) rename {Classification => medium_posts/Classification}/Cascade.py (100%) rename {Classification => medium_posts/Classification}/Classification.py (100%) rename {Classification => medium_posts/Classification}/Eval.py (100%) rename {Classification => medium_posts/Classification}/LogProbEval.py (100%) rename {Classification => medium_posts/Classification}/Model35.py (100%) rename {Classification => medium_posts/Classification}/Model4.py (100%) rename {Classification => medium_posts/Classification}/ModelDecorator.py (100%) rename CustomException.py => medium_posts/CustomException.py (100%) rename Dockerfile => medium_posts/Dockerfile (100%) rename Payload.py => medium_posts/Payload.py (100%) rename config.py => medium_posts/config.py (100%) rename {docTR => medium_posts/docTR}/Dockerfile (100%) rename {docTR => medium_posts/docTR}/README.md (100%) rename {docTR => medium_posts/docTR}/app.py (100%) rename main.py => medium_posts/main.py (100%) create mode 100644 medium_posts/requirements.txt rename utils.py => medium_posts/utils.py (94%) create mode 100644 pyproject.toml create mode 100644 tests/__init__.py create mode 100644 tests/document_loader_tesseract.py create mode 100644 tests/extractor.py create mode 100644 tests/models/invoice.py create mode 100644 tests/test_document_loader.py create mode 100644 tests/test_images/invoice.png diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..16520fc --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +ignore = E501 \ No newline at end of file diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml new file mode 100644 index 0000000..31c5eaa --- /dev/null +++ b/.github/workflows/workflow.yml @@ -0,0 +1,31 @@ +name: Python package workflow + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.8' + + - name: Install dependencies + run: | + pip install poetry + poetry install + + - name: Run tests + run: poetry run pytest + + - name: Build package + run: poetry build diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..3d757ea --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.7 # Ruff version + hooks: + - id: ruff # Run the linter. + name: Run Linter Check (Ruff) + args: [ --fix ] + files: ^(extractthinker|tests|examples)/ + - id: ruff-format # Run the formatter. + name: Run Formatter (Ruff) + - repo: local + hooks: + - id: ci_type_mypy + name: Run Type Check (Mypy) + entry: > + bash -c 'set -o pipefail; + export CUSTOM_PACKAGES="extractthinker/_types/_alias.py extractthinker/cli/cli.py extractthinker/cli/files.py extractthinker/cli/usage.py extractthinker/exceptions.py" && + export CUSTOM_FLAGS="--python-version=3.9 --color-output --no-pretty --follow-imports=skip" && + curl -sSL https://raw.githubusercontent.com/gao-hongnan/omniverse/2fd5de1b8103e955cd5f022ab016b72fa901fa8f/scripts/devops/continuous-integration/type_mypy.sh | + bash' + language: system + types: [python] + pass_filenames: false \ No newline at end of file diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..e6a022d --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,62 @@ +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", +] + +# Same as Black. +line-length = 88 +output-format = "grouped" + +target-version = "py39" + +[lint] +select = [ + # bugbear rules + "B", + # remove unused imports + "F401", + # bare except statements + "E722", + # unused arguments + "ARG", +] +ignore = [ + # mutable defaults + "B006", + "B018", +] + +unfixable = [ + # disable auto fix for print statements + "T201", + "T203", +] +ignore-init-module-imports = true + +[extend-per-file-ignores] +"instructor/distil.py" = ["ARG002"] +"tests/test_distil.py" = ["ARG001"] +"tests/test_patch.py" = ["ARG001"] +"examples/task_planner/task_planner_topological_sort.py" = ["ARG002"] +"examples/citation_with_extraction/main.py" = ["ARG001"] + diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6b76b4f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/extract_thinker/__init__.py b/extract_thinker/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/extract_thinker/app.py b/extract_thinker/app.py new file mode 100644 index 0000000..a4d6bb1 --- /dev/null +++ b/extract_thinker/app.py @@ -0,0 +1,51 @@ +from dotenv import load_dotenv + +from extract_thinker.document_loader.document_loader_tesseract import DocumentLoaderTesseract +from extractor import Extractor +from models import Classification + + +load_dotenv() + +classifications = [ + Classification(name="Driver License", description="This is a driver license"), + Classification(name="Invoice", description="This is an invoice"), +] + +# Usage +extractor = Extractor() + +# extractor.loadSplitter(ImageSplitter()) +# extractor.loadfile( +# "C:\\Users\\Lopez\\Desktop\\MagniFinance\\examples\\outputTestOne.pdf" +# ) +# extractor.split(classifications) + +# extractor.loadfile("C:\\Users\\Lopez\\Desktop\\MagniFinance\\examples\\outputTestOne.pdf").split(classifications) + +extractor.load_document_loader( + DocumentLoaderTesseract("C:\\Program Files\\Tesseract-OCR\\tesseract.exe") +) +extractor.load_llm("claude-3-haiku-20240307") + +# extractor.classify_from_path( +# "C:\\Users\\Lopez\\Desktop\\ExtractThinker\\driverLicense.jpg", +# classifications +# ) + +# extractor.loadfile( +# "C:\\Users\\Lopez\\Desktop\\ExtractThinker\\driverLicense.jpg" +# )\ +# .split(classifications)\ +# .extract()\ +# .where(lambda x: x.name == "Driver License")\ + +# user_info = extractor.extract_from_file( +# 'C:\\Users\\Lopez\\Desktop\\ExtractThinker\\driverLicense.jpg', UserContract, vision=True) + +# print(user_info.name) +# print(user_info.age) + +# the equivalent of this for the instructor: + +# equivalent for this, inside instructor: json.loads(json_string) \ No newline at end of file diff --git a/extract_thinker/document_loader/azure_form_recognizer_loader.py b/extract_thinker/document_loader/azure_form_recognizer_loader.py new file mode 100644 index 0000000..5bc3902 --- /dev/null +++ b/extract_thinker/document_loader/azure_form_recognizer_loader.py @@ -0,0 +1,11 @@ +from extract_thinker.document_loader.document_loader import DocumentLoader + + +class AzureFormRecognizerLoader(DocumentLoader): + def load_content_from_file(self, file_path): + # Implement this method for Azure Form Recognizer + pass + + def load_content_from_stream(self, stream): + # Implement this method for Azure Form Recognizer + pass diff --git a/extract_thinker/document_loader/doctr_loader.py b/extract_thinker/document_loader/doctr_loader.py new file mode 100644 index 0000000..d021b2a --- /dev/null +++ b/extract_thinker/document_loader/doctr_loader.py @@ -0,0 +1,11 @@ +from extract_thinker.document_loader.document_loader import DocumentLoader + + +class DocTRLoader(DocumentLoader): + def load_content_from_file(self, file_path): + # Implement this method for DocTR + pass + + def load_content_from_stream(self, stream): + # Implement this method for DocTR + pass diff --git a/extract_thinker/document_loader/document_loader.py b/extract_thinker/document_loader/document_loader.py new file mode 100644 index 0000000..625894a --- /dev/null +++ b/extract_thinker/document_loader/document_loader.py @@ -0,0 +1,67 @@ +from abc import ABC, abstractmethod +from PIL import Image +from io import BytesIO +import pypdfium2 as pdfium +import concurrent.futures +from typing import Any, Dict, List, Union + + +class DocumentLoader(ABC): + def __init__(self, content: Any = None): + self.content = content + self.file_path = None + + @abstractmethod + def load_content_from_file(self, file_path: str) -> Union[str, object]: + pass + + @abstractmethod + def load_content_from_stream(self, stream: BytesIO) -> Union[str, object]: + pass + + def getContent(self) -> Any: + return self.content + + def convert_pdf_to_images(self, file_path: str, scale: float = 300 / 72) -> List[Dict[int, bytes]]: + # Check if the file is already an image + try: + Image.open(file_path) + is_image = True + except IOError: + is_image = False + + if is_image: + # If it is, return it as is + with open(file_path, "rb") as f: + return [{0: f.read()}] + + # If it's not an image, proceed with the conversion + pdf_file = pdfium.PdfDocument(file_path) + + page_indices = [i for i in range(len(pdf_file))] + + with concurrent.futures.ThreadPoolExecutor() as executor: + futures = [] + for i in page_indices: + future = executor.submit(self.render_page, pdf_file, i, scale) + futures.append(future) + + final_images = [] + for future in concurrent.futures.as_completed(futures): + final_images.append(future.result()) + + return final_images + + @staticmethod + def render_page(pdf_file: pdfium.PdfDocument, page_index: int, scale: float) -> Dict[int, bytes]: + renderer = pdf_file.render( + pdfium.PdfBitmap.to_pil, + page_indices=[page_index], + scale=scale, + ) + image_list = list(renderer) + image = image_list[0] + image_byte_array = BytesIO() + image.save(image_byte_array, format="jpeg", optimize=True) + image_byte_array = image_byte_array.getvalue() + return {page_index: image_byte_array} diff --git a/extract_thinker/document_loader/document_loader_tesseract.py b/extract_thinker/document_loader/document_loader_tesseract.py new file mode 100644 index 0000000..63e8b06 --- /dev/null +++ b/extract_thinker/document_loader/document_loader_tesseract.py @@ -0,0 +1,49 @@ +from io import BytesIO +import os +from typing import Union +from PIL import Image +import pytesseract + +from extract_thinker.document_loader.document_loader import DocumentLoader + +from ..utils import get_image_type + +SUPPORTED_IMAGE_FORMATS = ["jpeg", "png", "bmp", "tiff"] + + +class DocumentLoaderTesseract(DocumentLoader): + def __init__(self, tesseract_cmd, isContainer=False, content=None): + self.content = content + self.tesseract_cmd = tesseract_cmd + if isContainer: + # docker path to tesseract + self.tesseract_cmd = os.environ.get("TESSERACT_PATH", "tesseract") + pytesseract.pytesseract.tesseract_cmd = self.tesseract_cmd + if not os.path.isfile(self.tesseract_cmd): + raise Exception(f"Tesseract not found at {self.tesseract_cmd}") + + def load_content_from_file(self, file_path: str) -> Union[str, object]: + try: + file_type = get_image_type(file_path) + if file_type in SUPPORTED_IMAGE_FORMATS: + image = Image.open(file_path) + raw_text = str(pytesseract.image_to_string(image)) + self.content = raw_text + return self.content + else: + raise Exception(f"Unsupported file type: {file_path}") + except Exception as e: + raise Exception(f"Error processing file: {e}") from e + + def load_content_from_stream(self, stream: Union[BytesIO, str]) -> Union[str, object]: + try: + file_type = get_image_type(stream) + if file_type in SUPPORTED_IMAGE_FORMATS: + image = Image.open(stream) + raw_text = str(pytesseract.image_to_string(image)) + self.content = raw_text + return self.content + else: + raise Exception(f"Unsupported stream type: {stream}") + except Exception as e: + raise Exception(f"Error processing stream: {e}") from e diff --git a/extract_thinker/document_loader/llm_interceptor.py b/extract_thinker/document_loader/llm_interceptor.py new file mode 100644 index 0000000..5a0bf1e --- /dev/null +++ b/extract_thinker/document_loader/llm_interceptor.py @@ -0,0 +1,7 @@ +from abc import ABC, abstractmethod + + +class LlmInterceptor(ABC): + @abstractmethod + def process(self, messages: list, response: str) -> None: + pass diff --git a/extract_thinker/document_loader/loader_interceptor.py b/extract_thinker/document_loader/loader_interceptor.py new file mode 100644 index 0000000..445f444 --- /dev/null +++ b/extract_thinker/document_loader/loader_interceptor.py @@ -0,0 +1,7 @@ +from abc import ABC, abstractmethod + + +class LoaderInterceptor(ABC): + @abstractmethod + def process(self, file: str, content: str) -> None: + raise NotImplementedError diff --git a/extract_thinker/document_loader/text_extract_loader.py b/extract_thinker/document_loader/text_extract_loader.py new file mode 100644 index 0000000..3eb93d1 --- /dev/null +++ b/extract_thinker/document_loader/text_extract_loader.py @@ -0,0 +1,11 @@ +from extract_thinker.document_loader.document_loader import DocumentLoader + + +class TextExtractLoader(DocumentLoader): + def load_content_from_file(self, file_path): + # Implement this method for TextExtract + pass + + def load_content_from_stream(self, stream): + # Implement this method for TextExtract + pass diff --git a/extract_thinker/extractor.py b/extract_thinker/extractor.py new file mode 100644 index 0000000..c52003e --- /dev/null +++ b/extract_thinker/extractor.py @@ -0,0 +1,243 @@ +from typing import List, Optional, IO, Union +from extract_thinker.document_loader.document_loader import DocumentLoader +from extract_thinker.models import ( + Classification, + ClassificationResponse, + DocGroups2, + DocGroup, + DocGroups, +) +from extract_thinker.splitter import Splitter +from extract_thinker.llm import LLM +import asyncio +import os + +from extract_thinker.document_loader.loader_interceptor import LoaderInterceptor +from extract_thinker.document_loader.llm_interceptor import LlmInterceptor + + +class Extractor: + def __init__( + self, processor: Optional[DocumentLoader] = None, llm: Optional[LLM] = None + ): + self.document_loader: Optional[DocumentLoader] = processor + self.llm: Optional[LLM] = llm + self.splitter: Optional[Splitter] = None + self.file: Optional[str] = None + doc_groups: Optional[DocGroups] = None + self.document_loaders_by_file_type = {} + self.loader_interceptors: List[LoaderInterceptor] = [] + self.llm_interceptors: List[LlmInterceptor] = [] + + def add_interceptor( + self, interceptor: Union[LoaderInterceptor, LlmInterceptor] + ) -> None: + if isinstance(interceptor, LoaderInterceptor): + self.loader_interceptors.append(interceptor) + elif isinstance(interceptor, LlmInterceptor): + self.llm_interceptors.append(interceptor) + else: + raise ValueError( + "Interceptor must be an instance of LoaderInterceptor or LlmInterceptor" + ) + + def set_document_loader_for_file_type( + self, file_type: str, document_loader: DocumentLoader + ): + self.document_loaders_by_file_type[file_type] = document_loader + + def get_document_loader_for_file(self, file: str) -> DocumentLoader: + _, ext = os.path.splitext(file) + return self.document_loaders_by_file_type.get(ext, self.document_loader) + + def load_document_loader(self, document_loader: DocumentLoader) -> None: + self.document_loader = document_loader + + def load_llm(self, model: str) -> None: + self.llm = LLM(model) + + def extract(self, source: Union[str, IO], response_model: str, vision: bool = False) -> str: + if isinstance(source, str): # if it's a file path + return self.extract_from_file(source, response_model, vision) + elif isinstance(source, IO): # if it's a stream + return self.extract_from_stream(source, response_model, vision) + else: + raise ValueError("Source must be a file path or a stream") + + def extract_from_file( + self, file: str, response_model: str, vision: bool = False + ) -> str: + if self.document_loader is not None: + content = self.document_loader.load_content_from_file(file) + else: + document_loader = self.get_document_loader_for_file(file) + if document_loader is None: + raise ValueError("No suitable document loader found for file type") + content = document_loader.load_content_from_file(file) + return self._extract(content, file, response_model, vision) + + def extract_from_stream( + self, stream: IO, response_model: str, vision: bool = False + ) -> str: + # check if document_loader is None, raise error + if self.document_loader is None: + raise ValueError("Document loader is not set") + + content = self.document_loader.load_content_from_stream(stream) + return self._extract(content, stream, response_model, vision, is_stream=True) + + def classify_from_path(self, path: str, classifications: List[Classification]): + content = self.document_loader.getContent(path) + return self._classify(content, classifications) + + def classify_from_stream(self, stream: IO, classifications: List[Classification]): + content = self.document_loader.getContentFromStream(stream) + self._classify(content, classifications) + + def _classify(self, content: str, classifications: List[Classification]): + messages = [ + { + "role": "system", + "content": "You are a server API that receives document information " + "and returns specific fields in JSON format.", + }, + ] + + input_data = ( + f"##Content\n{content[0]}\n##Classifications\n" + + "\n".join([f"{c.name}: {c.description}" for c in classifications]) + + "\n\n##JSON Output\n" + ) + + messages.append({"role": "user", "content": input_data}) + + response = self.llm.request(messages, ClassificationResponse) + + return response + + def _extract( + self, content, file_or_stream, response_model, vision=False, is_stream=False + ): + + #call all the llm interceptors before calling the llm + for interceptor in self.llm_interceptors: + interceptor.intercept(self.llm) + + messages = [ + { + "role": "system", + "content": "You are a server API that receives document information " + "and returns specific fields in JSON format.", + }, + ] + + if vision: + base64_encoded_image = self._encode_image_to_base64( + file_or_stream, is_stream + ) + + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "Whats in this image?"}, + { + "type": "image_url", + "image_url": { + "url": "data:image/jpeg;base64," + base64_encoded_image + }, + }, + ], + } + ] + else: + messages.append({"role": "user", "content": content}) + + response = self.llm.request(messages, response_model) + return response + + def split(self, classifications: List[Classification]): + splitter = self.splitter + + # Check if the file is a PDF + _, ext = os.path.splitext(self.file) + if ext.lower() != ".pdf": + raise ValueError("Invalid file type. Only PDFs are accepted.") + + images = self.document_loader.convert_pdf_to_images(self.file) + + groups = splitter.split_document_into_groups([self.file]) + + loop = asyncio.get_event_loop() + processedGroups = loop.run_until_complete( + splitter.process_split_groups(groups, classifications) + ) + + doc_groups = self.aggregate_split_documents_2(processedGroups) + + self.doc_groups = doc_groups + + return self + + def aggregate_split_documents_2(doc_groups_tasks: List[DocGroups2]) -> DocGroups: + doc_groups = DocGroups() + current_group = DocGroup() + page_number = 1 + + # do the first group outside of the loop + doc_group = doc_groups_tasks[0] + + if doc_group.belongs_to_same_document: + current_group.pages = [1, 2] + current_group.classification = doc_group.classification_page1 + current_group.certainties = [ + doc_group.certainty, + doc_groups_tasks[1].certainty, + ] + else: + current_group.pages = [1] + current_group.classification = doc_group.classification_page1 + current_group.certainties = [doc_group.certainty] + + doc_groups.doc_groups.append(current_group) + + current_group = DocGroup() + current_group.pages = [2] + current_group.classification = doc_group.classification_page2 + current_group.certainties = [doc_groups_tasks[1].certainty] + + page_number += 1 + + for index in range(1, len(doc_groups_tasks)): + doc_group_2 = doc_groups_tasks[index] + + if doc_group_2.belongs_to_same_document: + current_group.pages.append(page_number + 1) + current_group.certainties.append(doc_group_2.certainty) + else: + doc_groups.doc_groups.append(current_group) + + current_group = DocGroup() + current_group.classification = doc_group_2.classification_page2 + current_group.pages = [page_number + 1] + current_group.certainties = [doc_group_2.certainty] + + page_number += 1 + + doc_groups.doc_groups.append(current_group) # the last group + + return doc_groups + + def where(self, condition): + return self + + def loadfile(self, file): + self.file = file + return self + + def loadstream(self, stream): + return self + + def loadSplitter(self, splitter): + self.splitter = splitter + return self diff --git a/extract_thinker/image_splitter.py b/extract_thinker/image_splitter.py new file mode 100644 index 0000000..860cc4d --- /dev/null +++ b/extract_thinker/image_splitter.py @@ -0,0 +1,65 @@ +from ExtractThinker.models import Classification, DocGroups2 +from ExtractThinker.utils import encode_image +from splitter import Splitter + +import litellm +from PIL import Image + + +from typing import IO, List, Union + + +class ImageSplitter(Splitter): + def belongs_to_same_document(self, + page1: Union[str, IO], + page2: Union[str, IO], + classifications: List[Classification] + ) -> DocGroups2: + + assistantPrompt = 'What you are an API that extracts information. You receive as input: \r\n1. two pages \r\n2. a group of classifications\r\n output:\r\nA JSON with the classification of each document and if belongs to the same document\r\n\r\n//Example 1\r\n//can be null if belongsToSamePage is true\r\n{\r\n "belongsToSameDocument": true,\r\n "classificationPage1": "LLC",\r\n "classificationPage2": "LLC"\r\n}\r\n//Example 2\r\n{\r\n "belongsToSameDocument": false,\r\n "classificationPage1": "LLC",\r\n "classificationPage2": "Invoice"\r\n}' + + # make sure image1 and image2 are images and not text + try: + Image.open(page1) + Image.open(page2) + except IOError: + return {"error": "One or both of the input pages are not valid images."} + + base64_image1 = encode_image(page1) + base64_image2 = encode_image(page2) + + classifications_text = ( + "##Classifications\n" + + "\n".join([f"{c.name}: {c.description}" for c in classifications]) + + "\n\n##JSON Output\n" + ) + + resp = litellm.completion( + model="claude-3-sonnet-20240229", + messages=[ + { + "role": "system", + "content": assistantPrompt, + }, + { + "role": "user", + "content": [ + {"type": "text", "text": classifications_text}, + { + "type": "image_url", + "image_url": { + "url": "data:image/jpeg;base64," + base64_image1 + }, + }, + { + "type": "image_url", + "image_url": { + "url": "data:image/jpeg;base64," + base64_image2 + }, + }, + ], + }, + ], + ) + + return resp diff --git a/extract_thinker/llm.py b/extract_thinker/llm.py new file mode 100644 index 0000000..f65f826 --- /dev/null +++ b/extract_thinker/llm.py @@ -0,0 +1,16 @@ +import instructor +import litellm + + +class LLM: + def __init__(self, model): + self.client = instructor.from_litellm(litellm.completion) + self.model = model + + def request(self, messages, response_model): + return self.client.chat.completions.create( + model=self.model, + max_tokens=1024, + messages=messages, + response_model=response_model, + ) diff --git a/extract_thinker/models.py b/extract_thinker/models.py new file mode 100644 index 0000000..71939f7 --- /dev/null +++ b/extract_thinker/models.py @@ -0,0 +1,37 @@ +from typing import List, Optional +from pydantic import BaseModel +from dataclasses import dataclass + + +class Contract(BaseModel): + pass + + +class Classification(BaseModel): + name: str + description: str + contract: Optional[Contract] = None + + +class ClassificationResponse(BaseModel): + name: str + + +@dataclass +class DocGroups2: + certainty: float + belongs_to_same_document: bool + classification_page1: str + classification_page2: str + + +class DocGroup: + def __init__(self): + self.pages: List[int] = [] + self.classification: str = "" + self.certainties: List[float] = [] + + +class DocGroups: + def __init__(self): + self.doc_groups: List[DocGroup] = [] diff --git a/extract_thinker/splitter.py b/extract_thinker/splitter.py new file mode 100644 index 0000000..9a51af5 --- /dev/null +++ b/extract_thinker/splitter.py @@ -0,0 +1,43 @@ + +import asyncio +from abc import ABC, abstractmethod +from typing import IO, List, Union +from extract_thinker.models import Classification, DocGroups2 + + +class Splitter(ABC): + @abstractmethod + def belongs_to_same_document(self, + page1: Union[str, IO], + page2: Union[str, IO], + contract: str) -> DocGroups2: + pass + + def split_document_into_groups( + self, document: List[Union[str, IO]] + ) -> List[List[Union[str, IO]]]: + # Assuming document is a list of pages + page_per_split = 2 + split = [] + for i in range(0, len(document), page_per_split): + group = document[i: i + page_per_split] + # If last group has only one page, remove it + if len(group) != 1: + split.append(group) + return split + + async def process_split_groups(self, + split: List[List[Union[str, IO]]], + classifications: List[Classification] + ) -> List[DocGroups2]: + tasks = [self.process_group(x, classifications) for x in split] + doc_groups = await asyncio.gather(*tasks) + return doc_groups + + async def process_group(self, + group: List[Union[str, IO]], + contract: str) -> DocGroups2: + split_result = await self.belongs_to_same_document(group[0], + group[1], + contract) + return split_result diff --git a/extract_thinker/text_splitter.py b/extract_thinker/text_splitter.py new file mode 100644 index 0000000..3844749 --- /dev/null +++ b/extract_thinker/text_splitter.py @@ -0,0 +1,38 @@ +from splitter import Splitter + + +from typing import IO, Union + + +class TextSplitter(Splitter): + def belongs_to_same_document(self, + page1: Union[str, IO], + page2: Union[str, IO]) -> bool: + # assistantPrompt = 'What you are an API that extracts information. You receive as input: \r\n1. two pages \r\n2. a group of classifications\r\n output:\r\nA JSON with the classification of each document and if belongs to the same document\r\n\r\n//Example 1\r\n//can be null if belongsToSamePage is true\r\n{\r\n "belongsToSameDocument": true,\r\n "classificationPage1": "LLC",\r\n "classificationPage2": "LLC"\r\n}\r\n//Example 2\r\n{\r\n "belongsToSameDocument": false,\r\n "classificationPage1": "LLC",\r\n "classificationPage2": "Invoice"\r\n}' + + # classifications_text = ( + # "##Classifications\n" + # + "\n".join([f"{c.name}: {c.description}" for c in classifications]) + # + "\n\n##JSON Output\n" + # ) + + # resp = litellm.completion( + # model="claude-3-sonnet-20240229", + # messages=[ + # { + # "role": "system", + # "content": assistantPrompt, + # }, + # { + # "role": "user", + # "content": [ + # {"type": "text", "text": classifications_text}, + # {"type": "text", "text": page1}, + # {"type": "text", "text": page2}, + # ], + # }, + # ], + # ) + + # return resp + pass \ No newline at end of file diff --git a/extract_thinker/utils.py b/extract_thinker/utils.py new file mode 100644 index 0000000..3d6073f --- /dev/null +++ b/extract_thinker/utils.py @@ -0,0 +1,15 @@ +import base64 +from PIL import Image + + +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode("utf-8") + + +def get_image_type(image_path): + try: + img = Image.open(image_path) + return img.format.lower() + except IOError as e: + return f"An error occurred: {str(e)}" diff --git a/Antropic/AnthropicsApiRequest.py b/medium_posts/Antropic/AnthropicsApiRequest.py similarity index 100% rename from Antropic/AnthropicsApiRequest.py rename to medium_posts/Antropic/AnthropicsApiRequest.py diff --git a/Antropic/AnthropicsApiResponse.py b/medium_posts/Antropic/AnthropicsApiResponse.py similarity index 100% rename from Antropic/AnthropicsApiResponse.py rename to medium_posts/Antropic/AnthropicsApiResponse.py diff --git a/Antropic/AnthropicsApiService.py b/medium_posts/Antropic/AnthropicsApiService.py similarity index 100% rename from Antropic/AnthropicsApiService.py rename to medium_posts/Antropic/AnthropicsApiService.py diff --git a/Antropic/Content.py b/medium_posts/Antropic/Content.py similarity index 100% rename from Antropic/Content.py rename to medium_posts/Antropic/Content.py diff --git a/Antropic/ErrorDetail.py b/medium_posts/Antropic/ErrorDetail.py similarity index 100% rename from Antropic/ErrorDetail.py rename to medium_posts/Antropic/ErrorDetail.py diff --git a/Antropic/Message.py b/medium_posts/Antropic/Message.py similarity index 100% rename from Antropic/Message.py rename to medium_posts/Antropic/Message.py diff --git a/Antropic/Usage.py b/medium_posts/Antropic/Usage.py similarity index 100% rename from Antropic/Usage.py rename to medium_posts/Antropic/Usage.py diff --git a/Classification/Cascade.py b/medium_posts/Classification/Cascade.py similarity index 100% rename from Classification/Cascade.py rename to medium_posts/Classification/Cascade.py diff --git a/Classification/Classification.py b/medium_posts/Classification/Classification.py similarity index 100% rename from Classification/Classification.py rename to medium_posts/Classification/Classification.py diff --git a/Classification/Eval.py b/medium_posts/Classification/Eval.py similarity index 100% rename from Classification/Eval.py rename to medium_posts/Classification/Eval.py diff --git a/Classification/LogProbEval.py b/medium_posts/Classification/LogProbEval.py similarity index 100% rename from Classification/LogProbEval.py rename to medium_posts/Classification/LogProbEval.py diff --git a/Classification/Model35.py b/medium_posts/Classification/Model35.py similarity index 100% rename from Classification/Model35.py rename to medium_posts/Classification/Model35.py diff --git a/Classification/Model4.py b/medium_posts/Classification/Model4.py similarity index 100% rename from Classification/Model4.py rename to medium_posts/Classification/Model4.py diff --git a/Classification/ModelDecorator.py b/medium_posts/Classification/ModelDecorator.py similarity index 100% rename from Classification/ModelDecorator.py rename to medium_posts/Classification/ModelDecorator.py diff --git a/CustomException.py b/medium_posts/CustomException.py similarity index 100% rename from CustomException.py rename to medium_posts/CustomException.py diff --git a/Dockerfile b/medium_posts/Dockerfile similarity index 100% rename from Dockerfile rename to medium_posts/Dockerfile diff --git a/Payload.py b/medium_posts/Payload.py similarity index 100% rename from Payload.py rename to medium_posts/Payload.py diff --git a/config.py b/medium_posts/config.py similarity index 100% rename from config.py rename to medium_posts/config.py diff --git a/docTR/Dockerfile b/medium_posts/docTR/Dockerfile similarity index 100% rename from docTR/Dockerfile rename to medium_posts/docTR/Dockerfile diff --git a/docTR/README.md b/medium_posts/docTR/README.md similarity index 100% rename from docTR/README.md rename to medium_posts/docTR/README.md diff --git a/docTR/app.py b/medium_posts/docTR/app.py similarity index 100% rename from docTR/app.py rename to medium_posts/docTR/app.py diff --git a/main.py b/medium_posts/main.py similarity index 100% rename from main.py rename to medium_posts/main.py diff --git a/medium_posts/requirements.txt b/medium_posts/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..df1a24555c50272d2972685bba19eacd953db2a0 GIT binary patch literal 338 zcmXw!OAdlS5JYQj;!)gq0~2H7O0IwiVuE}JBOYFT-NS5V=$fk6#qV1w*QCJN>!GbS zn)TwVP^VF?D=%Ifv>SJDeR4h1qD)7fwD-Am)`FuTYpfneP|1dgbL72XPA@&g3hu1u zO30Zy*BVbH=SNqviJ8;Oy2KVE&H;vP39Htzxx{7r-*itLOSQP{|4fTh4cQV&jje06 ZRy(@Q5G>PshuHtJZaC=dq`NZ){Q;yrJGTG; literal 0 HcmV?d00001 diff --git a/utils.py b/medium_posts/utils.py similarity index 94% rename from utils.py rename to medium_posts/utils.py index 3b40b46..3c5ce74 100644 --- a/utils.py +++ b/medium_posts/utils.py @@ -1,10 +1,12 @@ import json + def remove_json_format(json_string: str) -> str: replace = json_string.replace("```json", "") replace = replace.replace("```", "") return replace.strip() + def remove_last_element(json_string: str) -> str: try: json.loads(json_string) @@ -19,4 +21,4 @@ def remove_last_element(json_string: str) -> str: trimmed_string = json_string[:last_index + 1] trimmed_string += "," - return trimmed_string \ No newline at end of file + return trimmed_string diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3f5aeb5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,29 @@ +[tool.poetry] +name = "extract_thinker" +version = "0.0.1" +description = "ORM style library to extract data from documents with LLMs" +authors = ["Júlio Almeida "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.9" +pydantic = "^2.7.0" +litellm = "^1.35.20" +pytesseract = "^0.3.10" +pillow = "^10.3.0" +python-docx = "^1.1.0" +xlrd = "^2.0.1" +pypdfium2 = "^4.29.0" +instructor = "^1.2.2" +python-dotenv = "^1.0.1" + +[tool.poetry.dev-dependencies] +flake8 = "^3.9.2" +black = "^21.9b0" + +[tool.poetry.group.dev.dependencies] +pytest = "^8.2.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index df1a24555c50272d2972685bba19eacd953db2a0..993e48e9d14f0b4d4a4a2d85b7ac4e44b77e6bbd 100644 GIT binary patch literal 85 zcmYkyF%Ez*2mrvHzqt4kztBL9BorE;vHiWexn8a>Dw>SI8tDVm9E7uqH0Ud9MD>CR X2aDMK(Pg@k;e;tpFLXM2wEe#yhgBfC literal 338 zcmXw!OAdlS5JYQj;!)gq0~2H7O0IwiVuE}JBOYFT-NS5V=$fk6#qV1w*QCJN>!GbS zn)TwVP^VF?D=%Ifv>SJDeR4h1qD)7fwD-Am)`FuTYpfneP|1dgbL72XPA@&g3hu1u zO30Zy*BVbH=SNqviJ8;Oy2KVE&H;vP39Htzxx{7r-*itLOSQP{|4fTh4cQV&jje06 ZRy(@Q5G>PshuHtJZaC=dq`NZ){Q;yrJGTG; diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/document_loader_tesseract.py b/tests/document_loader_tesseract.py new file mode 100644 index 0000000..159e262 --- /dev/null +++ b/tests/document_loader_tesseract.py @@ -0,0 +1,38 @@ +from io import BytesIO +import os +from dotenv import load_dotenv + +from extract_thinker.document_loader.document_loader_tesseract import DocumentLoaderTesseract + +# Get the current working directory +cwd = os.getcwd() + +load_dotenv() + +# Arrange +tesseract_path = os.getenv("TESSERACT_PATH") +loader = DocumentLoaderTesseract(tesseract_path) +test_file_path = os.path.join(cwd, "test_images", "invoice.png") + + +def test_load_content_from_file(): + # Act + content = loader.load_content_from_file(test_file_path) + + # Assert + assert content is not None + assert "Invoice" in content + assert "0000001" in content + + +def test_load_content_from_stream(): + with open(test_file_path, 'rb') as f: + test_image_stream = BytesIO(f.read()) + + # Act + content = loader.load_content_from_stream(test_image_stream) + + # Assert + assert content is not None + assert "Invoice" in content + assert "0000001" in content diff --git a/tests/extractor.py b/tests/extractor.py new file mode 100644 index 0000000..94a9fc4 --- /dev/null +++ b/tests/extractor.py @@ -0,0 +1,31 @@ + +import os +from dotenv import load_dotenv + +from extract_thinker.extractor import Extractor +from extract_thinker.document_loader.document_loader_tesseract import DocumentLoaderTesseract +from tests.models.invoice import InvoiceContract + +load_dotenv() +cwd = os.getcwd() + + +def test_extract_with_tessaract_and_claude(): + + # Arrange + tesseract_path = os.getenv("TESSERACT_PATH") + test_file_path = os.path.join(cwd, "test_images", "invoice.png") + + extractor = Extractor() + extractor.load_document_loader( + DocumentLoaderTesseract(tesseract_path) + ) + extractor.load_llm("claude-3-haiku-20240307") + + # Act + result = extractor.extract(test_file_path, InvoiceContract) + + # Assert + assert result is not None + assert result.invoice_number == "0000001" + assert result.invoice_date == "2014-05-07" diff --git a/tests/models/invoice.py b/tests/models/invoice.py new file mode 100644 index 0000000..36a3314 --- /dev/null +++ b/tests/models/invoice.py @@ -0,0 +1,6 @@ +from extract_thinker.models import Contract + + +class InvoiceContract(Contract): + invoice_number: str + invoice_date: str diff --git a/tests/test_document_loader.py b/tests/test_document_loader.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_images/invoice.png b/tests/test_images/invoice.png new file mode 100644 index 0000000000000000000000000000000000000000..c9f609f9ee2aab90c87fcc46fd9dfac0822c4738 GIT binary patch literal 47455 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 Lzbux1=l{O|6FvN# literal 0 HcmV?d00001 From 17b36859a658b8383d4d775f95c1663c4b34e571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Almeida?= Date: Tue, 30 Apr 2024 14:25:08 +0100 Subject: [PATCH 2/7] Cache added to DocumentLoader. poetry.lock --- .ruff.toml | 2 + .../document_loader/cached_document_loader.py | 19 + .../document_loader/document_loader.py | 4 +- .../document_loader_tesseract.py | 15 +- extract_thinker/extractor.py | 116 +- extract_thinker/featurelog.txt | 1 + extract_thinker/process.py | 98 + poetry.lock | 2154 +++++++++++++++++ pyproject.toml | 1 + tests/classify.py | 31 + tests/document_loader_tesseract.py | 9 + tests/test_document_loader.py | 0 12 files changed, 2353 insertions(+), 97 deletions(-) create mode 100644 extract_thinker/document_loader/cached_document_loader.py create mode 100644 extract_thinker/featurelog.txt create mode 100644 extract_thinker/process.py create mode 100644 poetry.lock create mode 100644 tests/classify.py delete mode 100644 tests/test_document_loader.py diff --git a/.ruff.toml b/.ruff.toml index e6a022d..359f446 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -39,6 +39,8 @@ select = [ "E722", # unused arguments "ARG", + # redefined variables + "ARG005", ] ignore = [ # mutable defaults diff --git a/extract_thinker/document_loader/cached_document_loader.py b/extract_thinker/document_loader/cached_document_loader.py new file mode 100644 index 0000000..f08bfa7 --- /dev/null +++ b/extract_thinker/document_loader/cached_document_loader.py @@ -0,0 +1,19 @@ + + +from io import BytesIO +from typing import Any, Union + +from cachetools import TTLCache +from extract_thinker.document_loader.document_loader import DocumentLoader + + +class CachedDocumentLoader(DocumentLoader): + def __init__(self, content: Any = None, cache_ttl: int = 300): + super().__init__(content) + self.cache = TTLCache(maxsize=100, ttl=cache_ttl) + + def cached_load_content_from_file(self, file_path: str) -> Union[str, object]: + return self.load_content_from_file(file_path) + + def cached_load_content_from_stream(self, stream: BytesIO) -> Union[str, object]: + return self.load_content_from_stream(stream) diff --git a/extract_thinker/document_loader/document_loader.py b/extract_thinker/document_loader/document_loader.py index 625894a..901a7b3 100644 --- a/extract_thinker/document_loader/document_loader.py +++ b/extract_thinker/document_loader/document_loader.py @@ -4,12 +4,14 @@ import pypdfium2 as pdfium import concurrent.futures from typing import Any, Dict, List, Union +from cachetools import TTLCache class DocumentLoader(ABC): - def __init__(self, content: Any = None): + def __init__(self, content: Any = None, cache_ttl: int = 300): self.content = content self.file_path = None + self.cache = TTLCache(maxsize=100, ttl=cache_ttl) @abstractmethod def load_content_from_file(self, file_path: str) -> Union[str, object]: diff --git a/extract_thinker/document_loader/document_loader_tesseract.py b/extract_thinker/document_loader/document_loader_tesseract.py index 63e8b06..5f6d274 100644 --- a/extract_thinker/document_loader/document_loader_tesseract.py +++ b/extract_thinker/document_loader/document_loader_tesseract.py @@ -1,19 +1,22 @@ from io import BytesIO +from operator import attrgetter import os from typing import Union from PIL import Image import pytesseract -from extract_thinker.document_loader.document_loader import DocumentLoader - +from extract_thinker.document_loader.cached_document_loader import CachedDocumentLoader from ..utils import get_image_type +from cachetools import cachedmethod +from cachetools.keys import hashkey + SUPPORTED_IMAGE_FORMATS = ["jpeg", "png", "bmp", "tiff"] -class DocumentLoaderTesseract(DocumentLoader): - def __init__(self, tesseract_cmd, isContainer=False, content=None): - self.content = content +class DocumentLoaderTesseract(CachedDocumentLoader): + def __init__(self, tesseract_cmd, isContainer=False, content=None, cache_ttl=300): + super().__init__(content, cache_ttl) self.tesseract_cmd = tesseract_cmd if isContainer: # docker path to tesseract @@ -22,6 +25,7 @@ def __init__(self, tesseract_cmd, isContainer=False, content=None): if not os.path.isfile(self.tesseract_cmd): raise Exception(f"Tesseract not found at {self.tesseract_cmd}") + @cachedmethod(cache=attrgetter('cache'), key=lambda self, file_path: hashkey(file_path)) def load_content_from_file(self, file_path: str) -> Union[str, object]: try: file_type = get_image_type(file_path) @@ -35,6 +39,7 @@ def load_content_from_file(self, file_path: str) -> Union[str, object]: except Exception as e: raise Exception(f"Error processing file: {e}") from e + @cachedmethod(cache=attrgetter('cache'), key=lambda self, stream: hashkey(id(stream))) def load_content_from_stream(self, stream: Union[BytesIO, str]) -> Union[str, object]: try: file_type = get_image_type(stream) diff --git a/extract_thinker/extractor.py b/extract_thinker/extractor.py index c52003e..623afa9 100644 --- a/extract_thinker/extractor.py +++ b/extract_thinker/extractor.py @@ -3,13 +3,9 @@ from extract_thinker.models import ( Classification, ClassificationResponse, - DocGroups2, - DocGroup, - DocGroups, ) from extract_thinker.splitter import Splitter from extract_thinker.llm import LLM -import asyncio import os from extract_thinker.document_loader.loader_interceptor import LoaderInterceptor @@ -24,7 +20,6 @@ def __init__( self.llm: Optional[LLM] = llm self.splitter: Optional[Splitter] = None self.file: Optional[str] = None - doc_groups: Optional[DocGroups] = None self.document_loaders_by_file_type = {} self.loader_interceptors: List[LoaderInterceptor] = [] self.llm_interceptors: List[LlmInterceptor] = [] @@ -56,7 +51,9 @@ def load_document_loader(self, document_loader: DocumentLoader) -> None: def load_llm(self, model: str) -> None: self.llm = LLM(model) - def extract(self, source: Union[str, IO], response_model: str, vision: bool = False) -> str: + def extract( + self, source: Union[str, IO], response_model: str, vision: bool = False + ) -> str: if isinstance(source, str): # if it's a file path return self.extract_from_file(source, response_model, vision) elif isinstance(source, IO): # if it's a stream @@ -83,15 +80,15 @@ def extract_from_stream( if self.document_loader is None: raise ValueError("Document loader is not set") - content = self.document_loader.load_content_from_stream(stream) + content = self.document_loader.load(stream) return self._extract(content, stream, response_model, vision, is_stream=True) def classify_from_path(self, path: str, classifications: List[Classification]): - content = self.document_loader.getContent(path) + content = self.document_loader.load_content_from_file(path) return self._classify(content, classifications) def classify_from_stream(self, stream: IO, classifications: List[Classification]): - content = self.document_loader.getContentFromStream(stream) + content = self.document_loader.load_content_from_stream(stream) self._classify(content, classifications) def _classify(self, content: str, classifications: List[Classification]): @@ -104,7 +101,7 @@ def _classify(self, content: str, classifications: List[Classification]): ] input_data = ( - f"##Content\n{content[0]}\n##Classifications\n" + f"##Content\n{content}\n##Classifications\n" + "\n".join([f"{c.name}: {c.description}" for c in classifications]) + "\n\n##JSON Output\n" ) @@ -115,11 +112,27 @@ def _classify(self, content: str, classifications: List[Classification]): return response + def classify(self, input: Union[str, IO]): + if isinstance(input, str): + # Check if the input is a valid file path + if os.path.isfile(input): + _, ext = os.path.splitext(input) + if ext.lower() == ".pdf": + return self.classify_from_path(input) + else: + raise ValueError(f"Unsupported file type: {ext}") + else: + raise ValueError(f"No such file: {input}") + elif hasattr(input, 'read'): + # Check if the input is a stream (like a file object) + return self.classify_from_stream(input) + else: + raise ValueError("Input must be a file path or a stream.") + def _extract( self, content, file_or_stream, response_model, vision=False, is_stream=False ): - - #call all the llm interceptors before calling the llm + # call all the llm interceptors before calling the llm for interceptor in self.llm_interceptors: interceptor.intercept(self.llm) @@ -156,88 +169,9 @@ def _extract( response = self.llm.request(messages, response_model) return response - def split(self, classifications: List[Classification]): - splitter = self.splitter - - # Check if the file is a PDF - _, ext = os.path.splitext(self.file) - if ext.lower() != ".pdf": - raise ValueError("Invalid file type. Only PDFs are accepted.") - - images = self.document_loader.convert_pdf_to_images(self.file) - - groups = splitter.split_document_into_groups([self.file]) - - loop = asyncio.get_event_loop() - processedGroups = loop.run_until_complete( - splitter.process_split_groups(groups, classifications) - ) - - doc_groups = self.aggregate_split_documents_2(processedGroups) - - self.doc_groups = doc_groups - - return self - - def aggregate_split_documents_2(doc_groups_tasks: List[DocGroups2]) -> DocGroups: - doc_groups = DocGroups() - current_group = DocGroup() - page_number = 1 - - # do the first group outside of the loop - doc_group = doc_groups_tasks[0] - - if doc_group.belongs_to_same_document: - current_group.pages = [1, 2] - current_group.classification = doc_group.classification_page1 - current_group.certainties = [ - doc_group.certainty, - doc_groups_tasks[1].certainty, - ] - else: - current_group.pages = [1] - current_group.classification = doc_group.classification_page1 - current_group.certainties = [doc_group.certainty] - - doc_groups.doc_groups.append(current_group) - - current_group = DocGroup() - current_group.pages = [2] - current_group.classification = doc_group.classification_page2 - current_group.certainties = [doc_groups_tasks[1].certainty] - - page_number += 1 - - for index in range(1, len(doc_groups_tasks)): - doc_group_2 = doc_groups_tasks[index] - - if doc_group_2.belongs_to_same_document: - current_group.pages.append(page_number + 1) - current_group.certainties.append(doc_group_2.certainty) - else: - doc_groups.doc_groups.append(current_group) - - current_group = DocGroup() - current_group.classification = doc_group_2.classification_page2 - current_group.pages = [page_number + 1] - current_group.certainties = [doc_group_2.certainty] - - page_number += 1 - - doc_groups.doc_groups.append(current_group) # the last group - - return doc_groups - - def where(self, condition): - return self - def loadfile(self, file): self.file = file return self def loadstream(self, stream): return self - - def loadSplitter(self, splitter): - self.splitter = splitter - return self diff --git a/extract_thinker/featurelog.txt b/extract_thinker/featurelog.txt new file mode 100644 index 0000000..0fc6113 --- /dev/null +++ b/extract_thinker/featurelog.txt @@ -0,0 +1 @@ +Add semantic search for the different languages and files diff --git a/extract_thinker/process.py b/extract_thinker/process.py new file mode 100644 index 0000000..6b44f14 --- /dev/null +++ b/extract_thinker/process.py @@ -0,0 +1,98 @@ +import asyncio +from typing import List, Optional +from extract_thinker.extractor import Extractor +from extract_thinker.models import Classification +from extract_thinker.models import ( + DocGroups2, + DocGroup, + DocGroups, +) +import os + + +class Process: + def __init__(self): + self.extractors: List[Extractor] = [] + doc_groups: Optional[DocGroups] = None + + def add_extractor(self, extractor: Extractor): + self.extractors.append(extractor) + + def loadSplitter(self, splitter): + self.splitter = splitter + return self + + def split(self, classifications: List[Classification]): + splitter = self.splitter + + # Check if the file is a PDF + _, ext = os.path.splitext(self.file) + if ext.lower() != ".pdf": + raise ValueError("Invalid file type. Only PDFs are accepted.") + + images = self.document_loader.convert_pdf_to_images(self.file) + + groups = splitter.split_document_into_groups([self.file]) + + loop = asyncio.get_event_loop() + processedGroups = loop.run_until_complete( + splitter.process_split_groups(groups, classifications) + ) + + doc_groups = self.aggregate_split_documents_2(processedGroups) + + self.doc_groups = doc_groups + + return self + + def aggregate_split_documents_2(doc_groups_tasks: List[DocGroups2]) -> DocGroups: + doc_groups = DocGroups() + current_group = DocGroup() + page_number = 1 + + # do the first group outside of the loop + doc_group = doc_groups_tasks[0] + + if doc_group.belongs_to_same_document: + current_group.pages = [1, 2] + current_group.classification = doc_group.classification_page1 + current_group.certainties = [ + doc_group.certainty, + doc_groups_tasks[1].certainty, + ] + else: + current_group.pages = [1] + current_group.classification = doc_group.classification_page1 + current_group.certainties = [doc_group.certainty] + + doc_groups.doc_groups.append(current_group) + + current_group = DocGroup() + current_group.pages = [2] + current_group.classification = doc_group.classification_page2 + current_group.certainties = [doc_groups_tasks[1].certainty] + + page_number += 1 + + for index in range(1, len(doc_groups_tasks)): + doc_group_2 = doc_groups_tasks[index] + + if doc_group_2.belongs_to_same_document: + current_group.pages.append(page_number + 1) + current_group.certainties.append(doc_group_2.certainty) + else: + doc_groups.doc_groups.append(current_group) + + current_group = DocGroup() + current_group.classification = doc_group_2.classification_page2 + current_group.pages = [page_number + 1] + current_group.certainties = [doc_group_2.certainty] + + page_number += 1 + + doc_groups.doc_groups.append(current_group) # the last group + + return doc_groups + + def where(self, condition): + pass diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..c2dfcf6 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,2154 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "aiohttp" +version = "3.9.5" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, + {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, + {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, + {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, + {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, + {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, + {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, + {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, + {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, + {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + +[[package]] +name = "anyio" +version = "4.3.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "black" +version = "21.12b0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.6.2" +files = [ + {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"}, + {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"}, +] + +[package.dependencies] +click = ">=7.1.2" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0,<1" +platformdirs = ">=2" +tomli = ">=0.2.6,<2.0.0" +typing-extensions = [ + {version = ">=3.10.0.0,<3.10.0.1 || >3.10.0.1", markers = "python_version >= \"3.10\""}, + {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}, +] + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +python2 = ["typed-ast (>=1.4.3)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "distro" +version = "1.9.0" +description = "Distro - an OS platform information API" +optional = false +python-versions = ">=3.6" +files = [ + {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, + {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, +] + +[[package]] +name = "docstring-parser" +version = "0.16" +description = "Parse Python docstrings in reST, Google and Numpydoc format" +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637"}, + {file = "docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.13.4" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, + {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "flake8" +version = "3.9.2" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, +] + +[package.dependencies] +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.7.0,<2.8.0" +pyflakes = ">=2.3.0,<2.4.0" + +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + +[[package]] +name = "fsspec" +version = "2024.3.1" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2024.3.1-py3-none-any.whl", hash = "sha256:918d18d41bf73f0e2b261824baeb1b124bcf771767e3a26425cd7dec3332f512"}, + {file = "fsspec-2024.3.1.tar.gz", hash = "sha256:f39780e282d7d117ffb42bb96992f8a90795e4d0fb0f661a70ca39fe9c43ded9"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +devel = ["pytest", "pytest-cov"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +tqdm = ["tqdm"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.5" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.26.0)"] + +[[package]] +name = "httpx" +version = "0.27.0" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "huggingface-hub" +version = "0.22.2" +description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "huggingface_hub-0.22.2-py3-none-any.whl", hash = "sha256:3429e25f38ccb834d310804a3b711e7e4953db5a9e420cc147a5e194ca90fd17"}, + {file = "huggingface_hub-0.22.2.tar.gz", hash = "sha256:32e9a9a6843c92f253ff9ca16b9985def4d80a93fb357af5353f770ef74a81be"}, +] + +[package.dependencies] +filelock = "*" +fsspec = ">=2023.5.0" +packaging = ">=20.9" +pyyaml = ">=5.1" +requests = "*" +tqdm = ">=4.42.1" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +cli = ["InquirerPy (==0.3.4)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] +hf-transfer = ["hf-transfer (>=0.1.4)"] +inference = ["aiohttp", "minijinja (>=1.0)"] +quality = ["mypy (==1.5.1)", "ruff (>=0.3.0)"] +tensorflow = ["graphviz", "pydot", "tensorflow"] +tensorflow-testing = ["keras (<3.0)", "tensorflow"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +torch = ["safetensors", "torch"] +typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "importlib-metadata" +version = "7.1.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "instructor" +version = "1.2.2" +description = "structured outputs for llm" +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "instructor-1.2.2-py3-none-any.whl", hash = "sha256:aff8144a58a29068720e6fdb4ccb5f09c6f72dc0813c100d137747a8f2a2ed03"}, + {file = "instructor-1.2.2.tar.gz", hash = "sha256:233e35685310bc3511eafe602534497b968712a29faaabbedc3af66a963c75fc"}, +] + +[package.dependencies] +aiohttp = ">=3.9.1,<4.0.0" +docstring-parser = ">=0.16,<0.17" +openai = ">=1.1.0,<2.0.0" +pydantic = "2.7.0" +pydantic-core = ">=2.18.0,<3.0.0" +rich = ">=13.7.0,<14.0.0" +tenacity = ">=8.2.3,<9.0.0" +typer = ">=0.9.0,<1.0.0" + +[package.extras] +anthropic = ["anthropic (>=0.23.1,<0.24.0)", "xmltodict (>=0.13.0,<0.14.0)"] +cohere = ["cohere (>=5.1.8,<6.0.0)"] +groq = ["groq (>=0.4.2,<0.5.0)"] +mistralai = ["mistralai (>=0.1.8,<0.2.0)"] +test-docs = ["anthropic (>=0.23.1,<0.24.0)", "cohere (>=5.1.8,<6.0.0)", "diskcache (>=5.6.3,<6.0.0)", "fastapi (>=0.109.2,<0.110.0)", "groq (>=0.4.2,<0.5.0)", "litellm (>=1.0.0,<2.0.0)", "mistralai (>=0.1.8,<0.2.0)", "pandas (>=2.2.0,<3.0.0)", "pydantic_extra_types (>=2.6.0,<3.0.0)", "redis (>=5.0.1,<6.0.0)", "tabulate (>=0.9.0,<0.10.0)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "litellm" +version = "1.35.29" +description = "Library to easily interface with LLM API providers" +optional = false +python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" +files = [ + {file = "litellm-1.35.29-py3-none-any.whl", hash = "sha256:756e3659a9001f7cbf331892e3b41ca7eaaf2ca373ca4b91556b308d902b331b"}, + {file = "litellm-1.35.29.tar.gz", hash = "sha256:05b89f605867d695e91d95c5f9b1545d00b60011ae425a8ea8562e994d71378a"}, +] + +[package.dependencies] +aiohttp = "*" +click = "*" +importlib-metadata = ">=6.8.0" +jinja2 = ">=3.1.2,<4.0.0" +openai = ">=1.0.0" +python-dotenv = ">=0.2.0" +requests = ">=2.31.0,<3.0.0" +tiktoken = ">=0.4.0" +tokenizers = "*" + +[package.extras] +extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "resend (>=0.8.0,<0.9.0)"] +proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.109.1,<0.110.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=21.2.0,<22.0.0)", "orjson (>=3.9.7,<4.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"] + +[[package]] +name = "lxml" +version = "5.2.1" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = false +python-versions = ">=3.6" +files = [ + {file = "lxml-5.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1f7785f4f789fdb522729ae465adcaa099e2a3441519df750ebdccc481d961a1"}, + {file = "lxml-5.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cc6ee342fb7fa2471bd9b6d6fdfc78925a697bf5c2bcd0a302e98b0d35bfad3"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:794f04eec78f1d0e35d9e0c36cbbb22e42d370dda1609fb03bcd7aeb458c6377"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817d420c60a5183953c783b0547d9eb43b7b344a2c46f69513d5952a78cddf3"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2213afee476546a7f37c7a9b4ad4d74b1e112a6fafffc9185d6d21f043128c81"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b070bbe8d3f0f6147689bed981d19bbb33070225373338df755a46893528104a"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e02c5175f63effbd7c5e590399c118d5db6183bbfe8e0d118bdb5c2d1b48d937"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3dc773b2861b37b41a6136e0b72a1a44689a9c4c101e0cddb6b854016acc0aa8"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:d7520db34088c96cc0e0a3ad51a4fd5b401f279ee112aa2b7f8f976d8582606d"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:bcbf4af004f98793a95355980764b3d80d47117678118a44a80b721c9913436a"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2b44bec7adf3e9305ce6cbfa47a4395667e744097faed97abb4728748ba7d47"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1c5bb205e9212d0ebddf946bc07e73fa245c864a5f90f341d11ce7b0b854475d"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2c9d147f754b1b0e723e6afb7ba1566ecb162fe4ea657f53d2139bbf894d050a"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:3545039fa4779be2df51d6395e91a810f57122290864918b172d5dc7ca5bb433"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a91481dbcddf1736c98a80b122afa0f7296eeb80b72344d7f45dc9f781551f56"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2ddfe41ddc81f29a4c44c8ce239eda5ade4e7fc305fb7311759dd6229a080052"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a7baf9ffc238e4bf401299f50e971a45bfcc10a785522541a6e3179c83eabf0a"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:31e9a882013c2f6bd2f2c974241bf4ba68c85eba943648ce88936d23209a2e01"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0a15438253b34e6362b2dc41475e7f80de76320f335e70c5528b7148cac253a1"}, + {file = "lxml-5.2.1-cp310-cp310-win32.whl", hash = "sha256:6992030d43b916407c9aa52e9673612ff39a575523c5f4cf72cdef75365709a5"}, + {file = "lxml-5.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:da052e7962ea2d5e5ef5bc0355d55007407087392cf465b7ad84ce5f3e25fe0f"}, + {file = "lxml-5.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:70ac664a48aa64e5e635ae5566f5227f2ab7f66a3990d67566d9907edcbbf867"}, + {file = "lxml-5.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1ae67b4e737cddc96c99461d2f75d218bdf7a0c3d3ad5604d1f5e7464a2f9ffe"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f18a5a84e16886898e51ab4b1d43acb3083c39b14c8caeb3589aabff0ee0b270"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6f2c8372b98208ce609c9e1d707f6918cc118fea4e2c754c9f0812c04ca116d"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:394ed3924d7a01b5bd9a0d9d946136e1c2f7b3dc337196d99e61740ed4bc6fe1"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d077bc40a1fe984e1a9931e801e42959a1e6598edc8a3223b061d30fbd26bbc"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:764b521b75701f60683500d8621841bec41a65eb739b8466000c6fdbc256c240"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3a6b45da02336895da82b9d472cd274b22dc27a5cea1d4b793874eead23dd14f"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:5ea7b6766ac2dfe4bcac8b8595107665a18ef01f8c8343f00710b85096d1b53a"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:e196a4ff48310ba62e53a8e0f97ca2bca83cdd2fe2934d8b5cb0df0a841b193a"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:200e63525948e325d6a13a76ba2911f927ad399ef64f57898cf7c74e69b71095"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dae0ed02f6b075426accbf6b2863c3d0a7eacc1b41fb40f2251d931e50188dad"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:ab31a88a651039a07a3ae327d68ebdd8bc589b16938c09ef3f32a4b809dc96ef"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:df2e6f546c4df14bc81f9498bbc007fbb87669f1bb707c6138878c46b06f6510"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5dd1537e7cc06efd81371f5d1a992bd5ab156b2b4f88834ca852de4a8ea523fa"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9b9ec9c9978b708d488bec36b9e4c94d88fd12ccac3e62134a9d17ddba910ea9"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8e77c69d5892cb5ba71703c4057091e31ccf534bd7f129307a4d084d90d014b8"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a8d5c70e04aac1eda5c829a26d1f75c6e5286c74743133d9f742cda8e53b9c2f"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c94e75445b00319c1fad60f3c98b09cd63fe1134a8a953dcd48989ef42318534"}, + {file = "lxml-5.2.1-cp311-cp311-win32.whl", hash = "sha256:4951e4f7a5680a2db62f7f4ab2f84617674d36d2d76a729b9a8be4b59b3659be"}, + {file = "lxml-5.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:5c670c0406bdc845b474b680b9a5456c561c65cf366f8db5a60154088c92d102"}, + {file = "lxml-5.2.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:abc25c3cab9ec7fcd299b9bcb3b8d4a1231877e425c650fa1c7576c5107ab851"}, + {file = "lxml-5.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6935bbf153f9a965f1e07c2649c0849d29832487c52bb4a5c5066031d8b44fd5"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d793bebb202a6000390a5390078e945bbb49855c29c7e4d56a85901326c3b5d9"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd5562927cdef7c4f5550374acbc117fd4ecc05b5007bdfa57cc5355864e0a4"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e7259016bc4345a31af861fdce942b77c99049d6c2107ca07dc2bba2435c1d9"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:530e7c04f72002d2f334d5257c8a51bf409db0316feee7c87e4385043be136af"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59689a75ba8d7ffca577aefd017d08d659d86ad4585ccc73e43edbfc7476781a"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f9737bf36262046213a28e789cc82d82c6ef19c85a0cf05e75c670a33342ac2c"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:3a74c4f27167cb95c1d4af1c0b59e88b7f3e0182138db2501c353555f7ec57f4"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:68a2610dbe138fa8c5826b3f6d98a7cfc29707b850ddcc3e21910a6fe51f6ca0"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f0a1bc63a465b6d72569a9bba9f2ef0334c4e03958e043da1920299100bc7c08"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c2d35a1d047efd68027817b32ab1586c1169e60ca02c65d428ae815b593e65d4"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:79bd05260359170f78b181b59ce871673ed01ba048deef4bf49a36ab3e72e80b"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:865bad62df277c04beed9478fe665b9ef63eb28fe026d5dedcb89b537d2e2ea6"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:44f6c7caff88d988db017b9b0e4ab04934f11e3e72d478031efc7edcac6c622f"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:71e97313406ccf55d32cc98a533ee05c61e15d11b99215b237346171c179c0b0"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:057cdc6b86ab732cf361f8b4d8af87cf195a1f6dc5b0ff3de2dced242c2015e0"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f3bbbc998d42f8e561f347e798b85513ba4da324c2b3f9b7969e9c45b10f6169"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:491755202eb21a5e350dae00c6d9a17247769c64dcf62d8c788b5c135e179dc4"}, + {file = "lxml-5.2.1-cp312-cp312-win32.whl", hash = "sha256:8de8f9d6caa7f25b204fc861718815d41cbcf27ee8f028c89c882a0cf4ae4134"}, + {file = "lxml-5.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:f2a9efc53d5b714b8df2b4b3e992accf8ce5bbdfe544d74d5c6766c9e1146a3a"}, + {file = "lxml-5.2.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:70a9768e1b9d79edca17890175ba915654ee1725975d69ab64813dd785a2bd5c"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c38d7b9a690b090de999835f0443d8aa93ce5f2064035dfc48f27f02b4afc3d0"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5670fb70a828663cc37552a2a85bf2ac38475572b0e9b91283dc09efb52c41d1"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:958244ad566c3ffc385f47dddde4145088a0ab893504b54b52c041987a8c1863"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6241d4eee5f89453307c2f2bfa03b50362052ca0af1efecf9fef9a41a22bb4f"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2a66bf12fbd4666dd023b6f51223aed3d9f3b40fef06ce404cb75bafd3d89536"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:9123716666e25b7b71c4e1789ec829ed18663152008b58544d95b008ed9e21e9"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:0c3f67e2aeda739d1cc0b1102c9a9129f7dc83901226cc24dd72ba275ced4218"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5d5792e9b3fb8d16a19f46aa8208987cfeafe082363ee2745ea8b643d9cc5b45"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:88e22fc0a6684337d25c994381ed8a1580a6f5ebebd5ad41f89f663ff4ec2885"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:21c2e6b09565ba5b45ae161b438e033a86ad1736b8c838c766146eff8ceffff9"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_s390x.whl", hash = "sha256:afbbdb120d1e78d2ba8064a68058001b871154cc57787031b645c9142b937a62"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:627402ad8dea044dde2eccde4370560a2b750ef894c9578e1d4f8ffd54000461"}, + {file = "lxml-5.2.1-cp36-cp36m-win32.whl", hash = "sha256:e89580a581bf478d8dcb97d9cd011d567768e8bc4095f8557b21c4d4c5fea7d0"}, + {file = "lxml-5.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:59565f10607c244bc4c05c0c5fa0c190c990996e0c719d05deec7030c2aa8289"}, + {file = "lxml-5.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:857500f88b17a6479202ff5fe5f580fc3404922cd02ab3716197adf1ef628029"}, + {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56c22432809085b3f3ae04e6e7bdd36883d7258fcd90e53ba7b2e463efc7a6af"}, + {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a55ee573116ba208932e2d1a037cc4b10d2c1cb264ced2184d00b18ce585b2c0"}, + {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:6cf58416653c5901e12624e4013708b6e11142956e7f35e7a83f1ab02f3fe456"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:64c2baa7774bc22dd4474248ba16fe1a7f611c13ac6123408694d4cc93d66dbd"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:74b28c6334cca4dd704e8004cba1955af0b778cf449142e581e404bd211fb619"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7221d49259aa1e5a8f00d3d28b1e0b76031655ca74bb287123ef56c3db92f213"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3dbe858ee582cbb2c6294dc85f55b5f19c918c2597855e950f34b660f1a5ede6"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:04ab5415bf6c86e0518d57240a96c4d1fcfc3cb370bb2ac2a732b67f579e5a04"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:6ab833e4735a7e5533711a6ea2df26459b96f9eec36d23f74cafe03631647c41"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f443cdef978430887ed55112b491f670bba6462cea7a7742ff8f14b7abb98d75"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:9e2addd2d1866fe112bc6f80117bcc6bc25191c5ed1bfbcf9f1386a884252ae8"}, + {file = "lxml-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:f51969bac61441fd31f028d7b3b45962f3ecebf691a510495e5d2cd8c8092dbd"}, + {file = "lxml-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b0b58fbfa1bf7367dde8a557994e3b1637294be6cf2169810375caf8571a085c"}, + {file = "lxml-5.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3e183c6e3298a2ed5af9d7a356ea823bccaab4ec2349dc9ed83999fd289d14d5"}, + {file = "lxml-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:804f74efe22b6a227306dd890eecc4f8c59ff25ca35f1f14e7482bbce96ef10b"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08802f0c56ed150cc6885ae0788a321b73505d2263ee56dad84d200cab11c07a"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8c09ed18ecb4ebf23e02b8e7a22a05d6411911e6fabef3a36e4f371f4f2585"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3d30321949861404323c50aebeb1943461a67cd51d4200ab02babc58bd06a86"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:b560e3aa4b1d49e0e6c847d72665384db35b2f5d45f8e6a5c0072e0283430533"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:058a1308914f20784c9f4674036527e7c04f7be6fb60f5d61353545aa7fcb739"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:adfb84ca6b87e06bc6b146dc7da7623395db1e31621c4785ad0658c5028b37d7"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:417d14450f06d51f363e41cace6488519038f940676ce9664b34ebf5653433a5"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a2dfe7e2473f9b59496247aad6e23b405ddf2e12ef0765677b0081c02d6c2c0b"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bf2e2458345d9bffb0d9ec16557d8858c9c88d2d11fed53998512504cd9df49b"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:58278b29cb89f3e43ff3e0c756abbd1518f3ee6adad9e35b51fb101c1c1daaec"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:64641a6068a16201366476731301441ce93457eb8452056f570133a6ceb15fca"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:78bfa756eab503673991bdcf464917ef7845a964903d3302c5f68417ecdc948c"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:11a04306fcba10cd9637e669fd73aa274c1c09ca64af79c041aa820ea992b637"}, + {file = "lxml-5.2.1-cp38-cp38-win32.whl", hash = "sha256:66bc5eb8a323ed9894f8fa0ee6cb3e3fb2403d99aee635078fd19a8bc7a5a5da"}, + {file = "lxml-5.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:9676bfc686fa6a3fa10cd4ae6b76cae8be26eb5ec6811d2a325636c460da1806"}, + {file = "lxml-5.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cf22b41fdae514ee2f1691b6c3cdeae666d8b7fa9434de445f12bbeee0cf48dd"}, + {file = "lxml-5.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec42088248c596dbd61d4ae8a5b004f97a4d91a9fd286f632e42e60b706718d7"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd53553ddad4a9c2f1f022756ae64abe16da1feb497edf4d9f87f99ec7cf86bd"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feaa45c0eae424d3e90d78823f3828e7dc42a42f21ed420db98da2c4ecf0a2cb"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddc678fb4c7e30cf830a2b5a8d869538bc55b28d6c68544d09c7d0d8f17694dc"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:853e074d4931dbcba7480d4dcab23d5c56bd9607f92825ab80ee2bd916edea53"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4691d60512798304acb9207987e7b2b7c44627ea88b9d77489bbe3e6cc3bd4"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:beb72935a941965c52990f3a32d7f07ce869fe21c6af8b34bf6a277b33a345d3"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:6588c459c5627fefa30139be4d2e28a2c2a1d0d1c265aad2ba1935a7863a4913"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:588008b8497667f1ddca7c99f2f85ce8511f8f7871b4a06ceede68ab62dff64b"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6787b643356111dfd4032b5bffe26d2f8331556ecb79e15dacb9275da02866e"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7c17b64b0a6ef4e5affae6a3724010a7a66bda48a62cfe0674dabd46642e8b54"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:27aa20d45c2e0b8cd05da6d4759649170e8dfc4f4e5ef33a34d06f2d79075d57"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d4f2cc7060dc3646632d7f15fe68e2fa98f58e35dd5666cd525f3b35d3fed7f8"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff46d772d5f6f73564979cd77a4fffe55c916a05f3cb70e7c9c0590059fb29ef"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:96323338e6c14e958d775700ec8a88346014a85e5de73ac7967db0367582049b"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:52421b41ac99e9d91934e4d0d0fe7da9f02bfa7536bb4431b4c05c906c8c6919"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7a7efd5b6d3e30d81ec68ab8a88252d7c7c6f13aaa875009fe3097eb4e30b84c"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ed777c1e8c99b63037b91f9d73a6aad20fd035d77ac84afcc205225f8f41188"}, + {file = "lxml-5.2.1-cp39-cp39-win32.whl", hash = "sha256:644df54d729ef810dcd0f7732e50e5ad1bd0a135278ed8d6bcb06f33b6b6f708"}, + {file = "lxml-5.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:9ca66b8e90daca431b7ca1408cae085d025326570e57749695d6a01454790e95"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b0ff53900566bc6325ecde9181d89afadc59c5ffa39bddf084aaedfe3b06a11"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd6037392f2d57793ab98d9e26798f44b8b4da2f2464388588f48ac52c489ea1"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9c07e7a45bb64e21df4b6aa623cb8ba214dfb47d2027d90eac197329bb5e94"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3249cc2989d9090eeac5467e50e9ec2d40704fea9ab72f36b034ea34ee65ca98"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f42038016852ae51b4088b2862126535cc4fc85802bfe30dea3500fdfaf1864e"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:533658f8fbf056b70e434dff7e7aa611bcacb33e01f75de7f821810e48d1bb66"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:622020d4521e22fb371e15f580d153134bfb68d6a429d1342a25f051ec72df1c"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efa7b51824aa0ee957ccd5a741c73e6851de55f40d807f08069eb4c5a26b2baa"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c6ad0fbf105f6bcc9300c00010a2ffa44ea6f555df1a2ad95c88f5656104817"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e233db59c8f76630c512ab4a4daf5a5986da5c3d5b44b8e9fc742f2a24dbd460"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a014510830df1475176466b6087fc0c08b47a36714823e58d8b8d7709132a96"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d38c8f50ecf57f0463399569aa388b232cf1a2ffb8f0a9a5412d0db57e054860"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5aea8212fb823e006b995c4dda533edcf98a893d941f173f6c9506126188860d"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff097ae562e637409b429a7ac958a20aab237a0378c42dabaa1e3abf2f896e5f"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f5d65c39f16717a47c36c756af0fb36144069c4718824b7533f803ecdf91138"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3d0c3dd24bb4605439bf91068598d00c6370684f8de4a67c2992683f6c309d6b"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e32be23d538753a8adb6c85bd539f5fd3b15cb987404327c569dfc5fd8366e85"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cc518cea79fd1e2f6c90baafa28906d4309d24f3a63e801d855e7424c5b34144"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a0af35bd8ebf84888373630f73f24e86bf016642fb8576fba49d3d6b560b7cbc"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8aca2e3a72f37bfc7b14ba96d4056244001ddcc18382bd0daa087fd2e68a354"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ca1e8188b26a819387b29c3895c47a5e618708fe6f787f3b1a471de2c4a94d9"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c8ba129e6d3b0136a0f50345b2cb3db53f6bda5dd8c7f5d83fbccba97fb5dcb5"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e998e304036198b4f6914e6a1e2b6f925208a20e2042563d9734881150c6c246"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d3be9b2076112e51b323bdf6d5a7f8a798de55fb8d95fcb64bd179460cdc0704"}, + {file = "lxml-5.2.1.tar.gz", hash = "sha256:3f7765e69bbce0906a7c74d5fe46d2c7a7596147318dbc08e4a2431f3060e306"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html-clean = ["lxml-html-clean"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=3.0.10)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = "*" +files = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "multidict" +version = "6.0.5" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "openai" +version = "1.23.6" +description = "The official Python library for the openai API" +optional = false +python-versions = ">=3.7.1" +files = [ + {file = "openai-1.23.6-py3-none-any.whl", hash = "sha256:f406c76ba279d16b9aca5a89cee0d968488e39f671f4dc6f0d690ac3c6f6fca1"}, + {file = "openai-1.23.6.tar.gz", hash = "sha256:612de2d54cf580920a1156273f84aada6b3dca26d048f62eb5364a4314d7f449"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tqdm = ">4" +typing-extensions = ">=4.7,<5" + +[package.extras] +datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pillow" +version = "10.3.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "platformdirs" +version = "4.2.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pycodestyle" +version = "2.7.0" +description = "Python style guide checker" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] + +[[package]] +name = "pydantic" +version = "2.7.0" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.7.0-py3-none-any.whl", hash = "sha256:9dee74a271705f14f9a1567671d144a851c675b072736f0a7b2608fd9e495352"}, + {file = "pydantic-2.7.0.tar.gz", hash = "sha256:b5ecdd42262ca2462e2624793551e80911a1e989f462910bb81aef974b4bb383"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.18.1" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.18.1" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ee9cf33e7fe14243f5ca6977658eb7d1042caaa66847daacbd2117adb258b226"}, + {file = "pydantic_core-2.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b7bbb97d82659ac8b37450c60ff2e9f97e4eb0f8a8a3645a5568b9334b08b50"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df4249b579e75094f7e9bb4bd28231acf55e308bf686b952f43100a5a0be394c"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d0491006a6ad20507aec2be72e7831a42efc93193d2402018007ff827dc62926"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ae80f72bb7a3e397ab37b53a2b49c62cc5496412e71bc4f1277620a7ce3f52b"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58aca931bef83217fca7a390e0486ae327c4af9c3e941adb75f8772f8eeb03a1"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1be91ad664fc9245404a789d60cba1e91c26b1454ba136d2a1bf0c2ac0c0505a"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:667880321e916a8920ef49f5d50e7983792cf59f3b6079f3c9dac2b88a311d17"}, + {file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f7054fdc556f5421f01e39cbb767d5ec5c1139ea98c3e5b350e02e62201740c7"}, + {file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:030e4f9516f9947f38179249778709a460a3adb516bf39b5eb9066fcfe43d0e6"}, + {file = "pydantic_core-2.18.1-cp310-none-win32.whl", hash = "sha256:2e91711e36e229978d92642bfc3546333a9127ecebb3f2761372e096395fc649"}, + {file = "pydantic_core-2.18.1-cp310-none-win_amd64.whl", hash = "sha256:9a29726f91c6cb390b3c2338f0df5cd3e216ad7a938762d11c994bb37552edb0"}, + {file = "pydantic_core-2.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9ece8a49696669d483d206b4474c367852c44815fca23ac4e48b72b339807f80"}, + {file = "pydantic_core-2.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a5d83efc109ceddb99abd2c1316298ced2adb4570410defe766851a804fcd5b"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7973c381283783cd1043a8c8f61ea5ce7a3a58b0369f0ee0ee975eaf2f2a1b"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54c7375c62190a7845091f521add19b0f026bcf6ae674bdb89f296972272e86d"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd63cec4e26e790b70544ae5cc48d11b515b09e05fdd5eff12e3195f54b8a586"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:561cf62c8a3498406495cfc49eee086ed2bb186d08bcc65812b75fda42c38294"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68717c38a68e37af87c4da20e08f3e27d7e4212e99e96c3d875fbf3f4812abfc"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d5728e93d28a3c63ee513d9ffbac9c5989de8c76e049dbcb5bfe4b923a9739d"}, + {file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f0f17814c505f07806e22b28856c59ac80cee7dd0fbb152aed273e116378f519"}, + {file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d816f44a51ba5175394bc6c7879ca0bd2be560b2c9e9f3411ef3a4cbe644c2e9"}, + {file = "pydantic_core-2.18.1-cp311-none-win32.whl", hash = "sha256:09f03dfc0ef8c22622eaa8608caa4a1e189cfb83ce847045eca34f690895eccb"}, + {file = "pydantic_core-2.18.1-cp311-none-win_amd64.whl", hash = "sha256:27f1009dc292f3b7ca77feb3571c537276b9aad5dd4efb471ac88a8bd09024e9"}, + {file = "pydantic_core-2.18.1-cp311-none-win_arm64.whl", hash = "sha256:48dd883db92e92519201f2b01cafa881e5f7125666141a49ffba8b9facc072b0"}, + {file = "pydantic_core-2.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b6b0e4912030c6f28bcb72b9ebe4989d6dc2eebcd2a9cdc35fefc38052dd4fe8"}, + {file = "pydantic_core-2.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3202a429fe825b699c57892d4371c74cc3456d8d71b7f35d6028c96dfecad31"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3982b0a32d0a88b3907e4b0dc36809fda477f0757c59a505d4e9b455f384b8b"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25595ac311f20e5324d1941909b0d12933f1fd2171075fcff763e90f43e92a0d"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14fe73881cf8e4cbdaded8ca0aa671635b597e42447fec7060d0868b52d074e6"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca976884ce34070799e4dfc6fbd68cb1d181db1eefe4a3a94798ddfb34b8867f"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684d840d2c9ec5de9cb397fcb3f36d5ebb6fa0d94734f9886032dd796c1ead06"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:54764c083bbe0264f0f746cefcded6cb08fbbaaf1ad1d78fb8a4c30cff999a90"}, + {file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:201713f2f462e5c015b343e86e68bd8a530a4f76609b33d8f0ec65d2b921712a"}, + {file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd1a9edb9dd9d79fbeac1ea1f9a8dd527a6113b18d2e9bcc0d541d308dae639b"}, + {file = "pydantic_core-2.18.1-cp312-none-win32.whl", hash = "sha256:d5e6b7155b8197b329dc787356cfd2684c9d6a6b1a197f6bbf45f5555a98d411"}, + {file = "pydantic_core-2.18.1-cp312-none-win_amd64.whl", hash = "sha256:9376d83d686ec62e8b19c0ac3bf8d28d8a5981d0df290196fb6ef24d8a26f0d6"}, + {file = "pydantic_core-2.18.1-cp312-none-win_arm64.whl", hash = "sha256:c562b49c96906b4029b5685075fe1ebd3b5cc2601dfa0b9e16c2c09d6cbce048"}, + {file = "pydantic_core-2.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3e352f0191d99fe617371096845070dee295444979efb8f27ad941227de6ad09"}, + {file = "pydantic_core-2.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0295d52b012cbe0d3059b1dba99159c3be55e632aae1999ab74ae2bd86a33d7"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56823a92075780582d1ffd4489a2e61d56fd3ebb4b40b713d63f96dd92d28144"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd3f79e17b56741b5177bcc36307750d50ea0698df6aa82f69c7db32d968c1c2"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38a5024de321d672a132b1834a66eeb7931959c59964b777e8f32dbe9523f6b1"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ce426ee691319d4767748c8e0895cfc56593d725594e415f274059bcf3cb76"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2adaeea59849ec0939af5c5d476935f2bab4b7f0335b0110f0f069a41024278e"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9b6431559676a1079eac0f52d6d0721fb8e3c5ba43c37bc537c8c83724031feb"}, + {file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:85233abb44bc18d16e72dc05bf13848a36f363f83757541f1a97db2f8d58cfd9"}, + {file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:641a018af4fe48be57a2b3d7a1f0f5dbca07c1d00951d3d7463f0ac9dac66622"}, + {file = "pydantic_core-2.18.1-cp38-none-win32.whl", hash = "sha256:63d7523cd95d2fde0d28dc42968ac731b5bb1e516cc56b93a50ab293f4daeaad"}, + {file = "pydantic_core-2.18.1-cp38-none-win_amd64.whl", hash = "sha256:907a4d7720abfcb1c81619863efd47c8a85d26a257a2dbebdb87c3b847df0278"}, + {file = "pydantic_core-2.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:aad17e462f42ddbef5984d70c40bfc4146c322a2da79715932cd8976317054de"}, + {file = "pydantic_core-2.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:94b9769ba435b598b547c762184bcfc4783d0d4c7771b04a3b45775c3589ca44"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80e0e57cc704a52fb1b48f16d5b2c8818da087dbee6f98d9bf19546930dc64b5"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76b86e24039c35280ceee6dce7e62945eb93a5175d43689ba98360ab31eebc4a"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a05db5013ec0ca4a32cc6433f53faa2a014ec364031408540ba858c2172bb0"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:250ae39445cb5475e483a36b1061af1bc233de3e9ad0f4f76a71b66231b07f88"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a32204489259786a923e02990249c65b0f17235073149d0033efcebe80095570"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6395a4435fa26519fd96fdccb77e9d00ddae9dd6c742309bd0b5610609ad7fb2"}, + {file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2533ad2883f001efa72f3d0e733fb846710c3af6dcdd544fe5bf14fa5fe2d7db"}, + {file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b560b72ed4816aee52783c66854d96157fd8175631f01ef58e894cc57c84f0f6"}, + {file = "pydantic_core-2.18.1-cp39-none-win32.whl", hash = "sha256:582cf2cead97c9e382a7f4d3b744cf0ef1a6e815e44d3aa81af3ad98762f5a9b"}, + {file = "pydantic_core-2.18.1-cp39-none-win_amd64.whl", hash = "sha256:ca71d501629d1fa50ea7fa3b08ba884fe10cefc559f5c6c8dfe9036c16e8ae89"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e178e5b66a06ec5bf51668ec0d4ac8cfb2bdcb553b2c207d58148340efd00143"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:72722ce529a76a4637a60be18bd789d8fb871e84472490ed7ddff62d5fed620d"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fe0c1ce5b129455e43f941f7a46f61f3d3861e571f2905d55cdbb8b5c6f5e2c"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4284c621f06a72ce2cb55f74ea3150113d926a6eb78ab38340c08f770eb9b4d"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a0c3e718f4e064efde68092d9d974e39572c14e56726ecfaeebbe6544521f47"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2027493cc44c23b598cfaf200936110433d9caa84e2c6cf487a83999638a96ac"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:76909849d1a6bffa5a07742294f3fa1d357dc917cb1fe7b470afbc3a7579d539"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ee7ccc7fb7e921d767f853b47814c3048c7de536663e82fbc37f5eb0d532224b"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee2794111c188548a4547eccc73a6a8527fe2af6cf25e1a4ebda2fd01cdd2e60"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a139fe9f298dc097349fb4f28c8b81cc7a202dbfba66af0e14be5cfca4ef7ce5"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d074b07a10c391fc5bbdcb37b2f16f20fcd9e51e10d01652ab298c0d07908ee2"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c69567ddbac186e8c0aadc1f324a60a564cfe25e43ef2ce81bcc4b8c3abffbae"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf1c7b78cddb5af00971ad5294a4583188bda1495b13760d9f03c9483bb6203"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2684a94fdfd1b146ff10689c6e4e815f6a01141781c493b97342cdc5b06f4d5d"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:73c1bc8a86a5c9e8721a088df234265317692d0b5cd9e86e975ce3bc3db62a59"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e60defc3c15defb70bb38dd605ff7e0fae5f6c9c7cbfe0ad7868582cb7e844a6"}, + {file = "pydantic_core-2.18.1.tar.gz", hash = "sha256:de9d3e8717560eb05e28739d1b35e4eac2e458553a52a301e51352a7ffc86a35"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pyflakes" +version = "2.3.1" +description = "passive checker of Python programs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pypdfium2" +version = "4.29.0" +description = "Python bindings to PDFium" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pypdfium2-4.29.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:e62cfc066bcd7f31ad45f56bc131d9d4bfcfb6eed58dc416246ddf0433a47586"}, + {file = "pypdfium2-4.29.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:33fa7cfebca8a7c78376b91f459a31bead6daf70080f1d9b35f16e1dd073f707"}, + {file = "pypdfium2-4.29.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:226464d87d5b391c968ea617d61b3076f35af1b4d48eb50a10eb2981cb24eb1f"}, + {file = "pypdfium2-4.29.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4531f37e7619efda528c16fe37b9296fb87d618263d736486c0360a3f394e1b2"}, + {file = "pypdfium2-4.29.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17d4fa7d891c8d7acf60fa8c641bb8738ef7dbdb3ff258391dfb1c2af49cfb1c"}, + {file = "pypdfium2-4.29.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae3bae000dc7fed925cdb2fb5303b122b0312f042c2d1557b66e8a7e4901957e"}, + {file = "pypdfium2-4.29.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:1a89641f352131ceb2ff604195a9fde4350debcb922d06c620cc42ccdd02c887"}, + {file = "pypdfium2-4.29.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:c3d5b57f97e32f828e437f5eaf23cb0e35f1103e6cfecd880c6d0ba9151b9ec3"}, + {file = "pypdfium2-4.29.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:935036f617f8fe5976575ff84e5dfacf205710914415ef285d677edfa7325d46"}, + {file = "pypdfium2-4.29.0-py3-none-win32.whl", hash = "sha256:e344354222843fd9a7fe756d3c65b66ca01ebee4eb634ed25ac0dfb72023564e"}, + {file = "pypdfium2-4.29.0-py3-none-win_amd64.whl", hash = "sha256:7d808f3c9fdbc4155ae008c502e63b93bf5aea569f6a62997590ef336f9932cb"}, + {file = "pypdfium2-4.29.0-py3-none-win_arm64.whl", hash = "sha256:1291a507fd4741399cfed819cecc126b2f1b7c8583dbb6a08b2f5300f91b86cc"}, + {file = "pypdfium2-4.29.0.tar.gz", hash = "sha256:e99d4c00a6b9123d48ab429c3d9ecac543f3f333ec54b7307089a58a67fca007"}, +] + +[[package]] +name = "pytesseract" +version = "0.3.10" +description = "Python-tesseract is a python wrapper for Google's Tesseract-OCR" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytesseract-0.3.10-py3-none-any.whl", hash = "sha256:8f22cc98f765bf13517ead0c70effedb46c153540d25783e04014f28b55a5fc6"}, + {file = "pytesseract-0.3.10.tar.gz", hash = "sha256:f1c3a8b0f07fd01a1085d451f5b8315be6eec1d5577a6796d46dc7a62bd4120f"}, +] + +[package.dependencies] +packaging = ">=21.3" +Pillow = ">=8.0.0" + +[[package]] +name = "pytest" +version = "8.2.0" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"}, + {file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-docx" +version = "1.1.0" +description = "Create, read, and update Microsoft Word .docx files." +optional = false +python-versions = ">=3.7" +files = [ + {file = "python-docx-1.1.0.tar.gz", hash = "sha256:5829b722141cf1ab79aedf0c34d9fe9924b29764584c0f2164eb2b02dcdf17c9"}, + {file = "python_docx-1.1.0-py3-none-any.whl", hash = "sha256:bac9773278098a1ddc43a52d84e22f5909c4a3080a624530b3ecb3771b07c6cd"}, +] + +[package.dependencies] +lxml = ">=3.1.0" +typing-extensions = "*" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "regex" +version = "2024.4.16" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.7" +files = [ + {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb83cc090eac63c006871fd24db5e30a1f282faa46328572661c0a24a2323a08"}, + {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c91e1763696c0eb66340c4df98623c2d4e77d0746b8f8f2bee2c6883fd1fe18"}, + {file = "regex-2024.4.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:10188fe732dec829c7acca7422cdd1bf57d853c7199d5a9e96bb4d40db239c73"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:956b58d692f235cfbf5b4f3abd6d99bf102f161ccfe20d2fd0904f51c72c4c66"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a70b51f55fd954d1f194271695821dd62054d949efd6368d8be64edd37f55c86"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c02fcd2bf45162280613d2e4a1ca3ac558ff921ae4e308ecb307650d3a6ee51"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ed75ea6892a56896d78f11006161eea52c45a14994794bcfa1654430984b22"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd727ad276bb91928879f3aa6396c9a1d34e5e180dce40578421a691eeb77f47"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7cbc5d9e8a1781e7be17da67b92580d6ce4dcef5819c1b1b89f49d9678cc278c"}, + {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:78fddb22b9ef810b63ef341c9fcf6455232d97cfe03938cbc29e2672c436670e"}, + {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:445ca8d3c5a01309633a0c9db57150312a181146315693273e35d936472df912"}, + {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:95399831a206211d6bc40224af1c635cb8790ddd5c7493e0bd03b85711076a53"}, + {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:7731728b6568fc286d86745f27f07266de49603a6fdc4d19c87e8c247be452af"}, + {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4facc913e10bdba42ec0aee76d029aedda628161a7ce4116b16680a0413f658a"}, + {file = "regex-2024.4.16-cp310-cp310-win32.whl", hash = "sha256:911742856ce98d879acbea33fcc03c1d8dc1106234c5e7d068932c945db209c0"}, + {file = "regex-2024.4.16-cp310-cp310-win_amd64.whl", hash = "sha256:e0a2df336d1135a0b3a67f3bbf78a75f69562c1199ed9935372b82215cddd6e2"}, + {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1210365faba7c2150451eb78ec5687871c796b0f1fa701bfd2a4a25420482d26"}, + {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ab40412f8cd6f615bfedea40c8bf0407d41bf83b96f6fc9ff34976d6b7037fd"}, + {file = "regex-2024.4.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fd80d1280d473500d8086d104962a82d77bfbf2b118053824b7be28cd5a79ea5"}, + {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb966fdd9217e53abf824f437a5a2d643a38d4fd5fd0ca711b9da683d452969"}, + {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20b7a68444f536365af42a75ccecb7ab41a896a04acf58432db9e206f4e525d6"}, + {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b74586dd0b039c62416034f811d7ee62810174bb70dffcca6439f5236249eb09"}, + {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8290b44d8b0af4e77048646c10c6e3aa583c1ca67f3b5ffb6e06cf0c6f0f89"}, + {file = "regex-2024.4.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2d80a6749724b37853ece57988b39c4e79d2b5fe2869a86e8aeae3bbeef9eb0"}, + {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3a1018e97aeb24e4f939afcd88211ace472ba566efc5bdf53fd8fd7f41fa7170"}, + {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8d015604ee6204e76569d2f44e5a210728fa917115bef0d102f4107e622b08d5"}, + {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:3d5ac5234fb5053850d79dd8eb1015cb0d7d9ed951fa37aa9e6249a19aa4f336"}, + {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:0a38d151e2cdd66d16dab550c22f9521ba79761423b87c01dae0a6e9add79c0d"}, + {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:159dc4e59a159cb8e4e8f8961eb1fa5d58f93cb1acd1701d8aff38d45e1a84a6"}, + {file = "regex-2024.4.16-cp311-cp311-win32.whl", hash = "sha256:ba2336d6548dee3117520545cfe44dc28a250aa091f8281d28804aa8d707d93d"}, + {file = "regex-2024.4.16-cp311-cp311-win_amd64.whl", hash = "sha256:8f83b6fd3dc3ba94d2b22717f9c8b8512354fd95221ac661784df2769ea9bba9"}, + {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80b696e8972b81edf0af2a259e1b2a4a661f818fae22e5fa4fa1a995fb4a40fd"}, + {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d61ae114d2a2311f61d90c2ef1358518e8f05eafda76eaf9c772a077e0b465ec"}, + {file = "regex-2024.4.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ba6745440b9a27336443b0c285d705ce73adb9ec90e2f2004c64d95ab5a7598"}, + {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295004b2dd37b0835ea5c14a33e00e8cfa3c4add4d587b77287825f3418d310"}, + {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4aba818dcc7263852aabb172ec27b71d2abca02a593b95fa79351b2774eb1d2b"}, + {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0800631e565c47520aaa04ae38b96abc5196fe8b4aa9bd864445bd2b5848a7a"}, + {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08dea89f859c3df48a440dbdcd7b7155bc675f2fa2ec8c521d02dc69e877db70"}, + {file = "regex-2024.4.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eeaa0b5328b785abc344acc6241cffde50dc394a0644a968add75fcefe15b9d4"}, + {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4e819a806420bc010489f4e741b3036071aba209f2e0989d4750b08b12a9343f"}, + {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c2d0e7cbb6341e830adcbfa2479fdeebbfbb328f11edd6b5675674e7a1e37730"}, + {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:91797b98f5e34b6a49f54be33f72e2fb658018ae532be2f79f7c63b4ae225145"}, + {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:d2da13568eff02b30fd54fccd1e042a70fe920d816616fda4bf54ec705668d81"}, + {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:370c68dc5570b394cbaadff50e64d705f64debed30573e5c313c360689b6aadc"}, + {file = "regex-2024.4.16-cp312-cp312-win32.whl", hash = "sha256:904c883cf10a975b02ab3478bce652f0f5346a2c28d0a8521d97bb23c323cc8b"}, + {file = "regex-2024.4.16-cp312-cp312-win_amd64.whl", hash = "sha256:785c071c982dce54d44ea0b79cd6dfafddeccdd98cfa5f7b86ef69b381b457d9"}, + {file = "regex-2024.4.16-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e2f142b45c6fed48166faeb4303b4b58c9fcd827da63f4cf0a123c3480ae11fb"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87ab229332ceb127a165612d839ab87795972102cb9830e5f12b8c9a5c1b508"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81500ed5af2090b4a9157a59dbc89873a25c33db1bb9a8cf123837dcc9765047"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b340cccad138ecb363324aa26893963dcabb02bb25e440ebdf42e30963f1a4e0"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c72608e70f053643437bd2be0608f7f1c46d4022e4104d76826f0839199347a"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a01fe2305e6232ef3e8f40bfc0f0f3a04def9aab514910fa4203bafbc0bb4682"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:03576e3a423d19dda13e55598f0fd507b5d660d42c51b02df4e0d97824fdcae3"}, + {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:549c3584993772e25f02d0656ac48abdda73169fe347263948cf2b1cead622f3"}, + {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:34422d5a69a60b7e9a07a690094e824b66f5ddc662a5fc600d65b7c174a05f04"}, + {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:5f580c651a72b75c39e311343fe6875d6f58cf51c471a97f15a938d9fe4e0d37"}, + {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3399dd8a7495bbb2bacd59b84840eef9057826c664472e86c91d675d007137f5"}, + {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d1f86f3f4e2388aa3310b50694ac44daefbd1681def26b4519bd050a398dc5a"}, + {file = "regex-2024.4.16-cp37-cp37m-win32.whl", hash = "sha256:dd5acc0a7d38fdc7a3a6fd3ad14c880819008ecb3379626e56b163165162cc46"}, + {file = "regex-2024.4.16-cp37-cp37m-win_amd64.whl", hash = "sha256:ba8122e3bb94ecda29a8de4cf889f600171424ea586847aa92c334772d200331"}, + {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:743deffdf3b3481da32e8a96887e2aa945ec6685af1cfe2bcc292638c9ba2f48"}, + {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7571f19f4a3fd00af9341c7801d1ad1967fc9c3f5e62402683047e7166b9f2b4"}, + {file = "regex-2024.4.16-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:df79012ebf6f4efb8d307b1328226aef24ca446b3ff8d0e30202d7ebcb977a8c"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e757d475953269fbf4b441207bb7dbdd1c43180711b6208e129b637792ac0b93"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4313ab9bf6a81206c8ac28fdfcddc0435299dc88cad12cc6305fd0e78b81f9e4"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d83c2bc678453646f1a18f8db1e927a2d3f4935031b9ad8a76e56760461105dd"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9df1bfef97db938469ef0a7354b2d591a2d438bc497b2c489471bec0e6baf7c4"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62120ed0de69b3649cc68e2965376048793f466c5a6c4370fb27c16c1beac22d"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2ef6f7990b6e8758fe48ad08f7e2f66c8f11dc66e24093304b87cae9037bb4a"}, + {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8fc6976a3395fe4d1fbeb984adaa8ec652a1e12f36b56ec8c236e5117b585427"}, + {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:03e68f44340528111067cecf12721c3df4811c67268b897fbe695c95f860ac42"}, + {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ec7e0043b91115f427998febaa2beb82c82df708168b35ece3accb610b91fac1"}, + {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c21fc21a4c7480479d12fd8e679b699f744f76bb05f53a1d14182b31f55aac76"}, + {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:12f6a3f2f58bb7344751919a1876ee1b976fe08b9ffccb4bbea66f26af6017b9"}, + {file = "regex-2024.4.16-cp38-cp38-win32.whl", hash = "sha256:479595a4fbe9ed8f8f72c59717e8cf222da2e4c07b6ae5b65411e6302af9708e"}, + {file = "regex-2024.4.16-cp38-cp38-win_amd64.whl", hash = "sha256:0534b034fba6101611968fae8e856c1698da97ce2efb5c2b895fc8b9e23a5834"}, + {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7ccdd1c4a3472a7533b0a7aa9ee34c9a2bef859ba86deec07aff2ad7e0c3b94"}, + {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f2f017c5be19984fbbf55f8af6caba25e62c71293213f044da3ada7091a4455"}, + {file = "regex-2024.4.16-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:803b8905b52de78b173d3c1e83df0efb929621e7b7c5766c0843704d5332682f"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:684008ec44ad275832a5a152f6e764bbe1914bea10968017b6feaecdad5736e0"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65436dce9fdc0aeeb0a0effe0839cb3d6a05f45aa45a4d9f9c60989beca78b9c"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea355eb43b11764cf799dda62c658c4d2fdb16af41f59bb1ccfec517b60bcb07"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c1165f3809ce7774f05cb74e5408cd3aa93ee8573ae959a97a53db3ca3180d"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cccc79a9be9b64c881f18305a7c715ba199e471a3973faeb7ba84172abb3f317"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00169caa125f35d1bca6045d65a662af0202704489fada95346cfa092ec23f39"}, + {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6cc38067209354e16c5609b66285af17a2863a47585bcf75285cab33d4c3b8df"}, + {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:23cff1b267038501b179ccbbd74a821ac4a7192a1852d1d558e562b507d46013"}, + {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d320b3bf82a39f248769fc7f188e00f93526cc0fe739cfa197868633d44701"}, + {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:89ec7f2c08937421bbbb8b48c54096fa4f88347946d4747021ad85f1b3021b3c"}, + {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4918fd5f8b43aa7ec031e0fef1ee02deb80b6afd49c85f0790be1dc4ce34cb50"}, + {file = "regex-2024.4.16-cp39-cp39-win32.whl", hash = "sha256:684e52023aec43bdf0250e843e1fdd6febbe831bd9d52da72333fa201aaa2335"}, + {file = "regex-2024.4.16-cp39-cp39-win_amd64.whl", hash = "sha256:e697e1c0238133589e00c244a8b676bc2cfc3ab4961318d902040d099fec7483"}, + {file = "regex-2024.4.16.tar.gz", hash = "sha256:fa454d26f2e87ad661c4f0c5a5fe4cf6aab1e307d1b94f16ffdfcb089ba685c0"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "tenacity" +version = "8.2.3" +description = "Retry code until it succeeds" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, + {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, +] + +[package.extras] +doc = ["reno", "sphinx", "tornado (>=4.5)"] + +[[package]] +name = "tiktoken" +version = "0.6.0" +description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tiktoken-0.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:277de84ccd8fa12730a6b4067456e5cf72fef6300bea61d506c09e45658d41ac"}, + {file = "tiktoken-0.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c44433f658064463650d61387623735641dcc4b6c999ca30bc0f8ba3fccaf5c"}, + {file = "tiktoken-0.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afb9a2a866ae6eef1995ab656744287a5ac95acc7e0491c33fad54d053288ad3"}, + {file = "tiktoken-0.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c62c05b3109fefca26fedb2820452a050074ad8e5ad9803f4652977778177d9f"}, + {file = "tiktoken-0.6.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ef917fad0bccda07bfbad835525bbed5f3ab97a8a3e66526e48cdc3e7beacf7"}, + {file = "tiktoken-0.6.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e095131ab6092d0769a2fda85aa260c7c383072daec599ba9d8b149d2a3f4d8b"}, + {file = "tiktoken-0.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:05b344c61779f815038292a19a0c6eb7098b63c8f865ff205abb9ea1b656030e"}, + {file = "tiktoken-0.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cefb9870fb55dca9e450e54dbf61f904aab9180ff6fe568b61f4db9564e78871"}, + {file = "tiktoken-0.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:702950d33d8cabc039845674107d2e6dcabbbb0990ef350f640661368df481bb"}, + {file = "tiktoken-0.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d49d076058f23254f2aff9af603863c5c5f9ab095bc896bceed04f8f0b013a"}, + {file = "tiktoken-0.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:430bc4e650a2d23a789dc2cdca3b9e5e7eb3cd3935168d97d43518cbb1f9a911"}, + {file = "tiktoken-0.6.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:293cb8669757301a3019a12d6770bd55bec38a4d3ee9978ddbe599d68976aca7"}, + {file = "tiktoken-0.6.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7bd1a288b7903aadc054b0e16ea78e3171f70b670e7372432298c686ebf9dd47"}, + {file = "tiktoken-0.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac76e000183e3b749634968a45c7169b351e99936ef46f0d2353cd0d46c3118d"}, + {file = "tiktoken-0.6.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17cc8a4a3245ab7d935c83a2db6bb71619099d7284b884f4b2aea4c74f2f83e3"}, + {file = "tiktoken-0.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:284aebcccffe1bba0d6571651317df6a5b376ff6cfed5aeb800c55df44c78177"}, + {file = "tiktoken-0.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c1a3a5d33846f8cd9dd3b7897c1d45722f48625a587f8e6f3d3e85080559be8"}, + {file = "tiktoken-0.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6318b2bb2337f38ee954fd5efa82632c6e5ced1d52a671370fa4b2eff1355e91"}, + {file = "tiktoken-0.6.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f5f0f2ed67ba16373f9a6013b68da298096b27cd4e1cf276d2d3868b5c7efd1"}, + {file = "tiktoken-0.6.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:75af4c0b16609c2ad02581f3cdcd1fb698c7565091370bf6c0cf8624ffaba6dc"}, + {file = "tiktoken-0.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:45577faf9a9d383b8fd683e313cf6df88b6076c034f0a16da243bb1c139340c3"}, + {file = "tiktoken-0.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7c1492ab90c21ca4d11cef3a236ee31a3e279bb21b3fc5b0e2210588c4209e68"}, + {file = "tiktoken-0.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e2b380c5b7751272015400b26144a2bab4066ebb8daae9c3cd2a92c3b508fe5a"}, + {file = "tiktoken-0.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f497598b9f58c99cbc0eb764b4a92272c14d5203fc713dd650b896a03a50ad"}, + {file = "tiktoken-0.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e65e8bd6f3f279d80f1e1fbd5f588f036b9a5fa27690b7f0cc07021f1dfa0839"}, + {file = "tiktoken-0.6.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5f1495450a54e564d236769d25bfefbf77727e232d7a8a378f97acddee08c1ae"}, + {file = "tiktoken-0.6.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6c4e4857d99f6fb4670e928250835b21b68c59250520a1941618b5b4194e20c3"}, + {file = "tiktoken-0.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:168d718f07a39b013032741867e789971346df8e89983fe3c0ef3fbd5a0b1cb9"}, + {file = "tiktoken-0.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:47fdcfe11bd55376785a6aea8ad1db967db7f66ea81aed5c43fad497521819a4"}, + {file = "tiktoken-0.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fb7d2ccbf1a7784810aff6b80b4012fb42c6fc37eaa68cb3b553801a5cc2d1fc"}, + {file = "tiktoken-0.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ccb7a111ee76af5d876a729a347f8747d5ad548e1487eeea90eaf58894b3138"}, + {file = "tiktoken-0.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2048e1086b48e3c8c6e2ceeac866561374cd57a84622fa49a6b245ffecb7744"}, + {file = "tiktoken-0.6.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07f229a5eb250b6403a61200199cecf0aac4aa23c3ecc1c11c1ca002cbb8f159"}, + {file = "tiktoken-0.6.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:432aa3be8436177b0db5a2b3e7cc28fd6c693f783b2f8722539ba16a867d0c6a"}, + {file = "tiktoken-0.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:8bfe8a19c8b5c40d121ee7938cd9c6a278e5b97dc035fd61714b4f0399d2f7a1"}, + {file = "tiktoken-0.6.0.tar.gz", hash = "sha256:ace62a4ede83c75b0374a2ddfa4b76903cf483e9cb06247f566be3bf14e6beed"}, +] + +[package.dependencies] +regex = ">=2022.1.18" +requests = ">=2.26.0" + +[package.extras] +blobfile = ["blobfile (>=2)"] + +[[package]] +name = "tokenizers" +version = "0.19.1" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tokenizers-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:952078130b3d101e05ecfc7fc3640282d74ed26bcf691400f872563fca15ac97"}, + {file = "tokenizers-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82c8b8063de6c0468f08e82c4e198763e7b97aabfe573fd4cf7b33930ca4df77"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f03727225feaf340ceeb7e00604825addef622d551cbd46b7b775ac834c1e1c4"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:453e4422efdfc9c6b6bf2eae00d5e323f263fff62b29a8c9cd526c5003f3f642"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:02e81bf089ebf0e7f4df34fa0207519f07e66d8491d963618252f2e0729e0b46"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b07c538ba956843833fee1190cf769c60dc62e1cf934ed50d77d5502194d63b1"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28cab1582e0eec38b1f38c1c1fb2e56bce5dc180acb1724574fc5f47da2a4fe"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b01afb7193d47439f091cd8f070a1ced347ad0f9144952a30a41836902fe09e"}, + {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7fb297edec6c6841ab2e4e8f357209519188e4a59b557ea4fafcf4691d1b4c98"}, + {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e8a3dd055e515df7054378dc9d6fa8c8c34e1f32777fb9a01fea81496b3f9d3"}, + {file = "tokenizers-0.19.1-cp310-none-win32.whl", hash = "sha256:7ff898780a155ea053f5d934925f3902be2ed1f4d916461e1a93019cc7250837"}, + {file = "tokenizers-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:bea6f9947e9419c2fda21ae6c32871e3d398cba549b93f4a65a2d369662d9403"}, + {file = "tokenizers-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5c88d1481f1882c2e53e6bb06491e474e420d9ac7bdff172610c4f9ad3898059"}, + {file = "tokenizers-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddf672ed719b4ed82b51499100f5417d7d9f6fb05a65e232249268f35de5ed14"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dadc509cc8a9fe460bd274c0e16ac4184d0958117cf026e0ea8b32b438171594"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfedf31824ca4915b511b03441784ff640378191918264268e6923da48104acc"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac11016d0a04aa6487b1513a3a36e7bee7eec0e5d30057c9c0408067345c48d2"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76951121890fea8330d3a0df9a954b3f2a37e3ec20e5b0530e9a0044ca2e11fe"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b342d2ce8fc8d00f376af068e3274e2e8649562e3bc6ae4a67784ded6b99428d"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d16ff18907f4909dca9b076b9c2d899114dd6abceeb074eca0c93e2353f943aa"}, + {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:706a37cc5332f85f26efbe2bdc9ef8a9b372b77e4645331a405073e4b3a8c1c6"}, + {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16baac68651701364b0289979ecec728546133e8e8fe38f66fe48ad07996b88b"}, + {file = "tokenizers-0.19.1-cp311-none-win32.whl", hash = "sha256:9ed240c56b4403e22b9584ee37d87b8bfa14865134e3e1c3fb4b2c42fafd3256"}, + {file = "tokenizers-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:ad57d59341710b94a7d9dbea13f5c1e7d76fd8d9bcd944a7a6ab0b0da6e0cc66"}, + {file = "tokenizers-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:621d670e1b1c281a1c9698ed89451395d318802ff88d1fc1accff0867a06f153"}, + {file = "tokenizers-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d924204a3dbe50b75630bd16f821ebda6a5f729928df30f582fb5aade90c818a"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4f3fefdc0446b1a1e6d81cd4c07088ac015665d2e812f6dbba4a06267d1a2c95"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9620b78e0b2d52ef07b0d428323fb34e8ea1219c5eac98c2596311f20f1f9266"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04ce49e82d100594715ac1b2ce87d1a36e61891a91de774755f743babcd0dd52"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5c2ff13d157afe413bf7e25789879dd463e5a4abfb529a2d8f8473d8042e28f"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3174c76efd9d08f836bfccaca7cfec3f4d1c0a4cf3acbc7236ad577cc423c840"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c9d5b6c0e7a1e979bec10ff960fae925e947aab95619a6fdb4c1d8ff3708ce3"}, + {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a179856d1caee06577220ebcfa332af046d576fb73454b8f4d4b0ba8324423ea"}, + {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:952b80dac1a6492170f8c2429bd11fcaa14377e097d12a1dbe0ef2fb2241e16c"}, + {file = "tokenizers-0.19.1-cp312-none-win32.whl", hash = "sha256:01d62812454c188306755c94755465505836fd616f75067abcae529c35edeb57"}, + {file = "tokenizers-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:b70bfbe3a82d3e3fb2a5e9b22a39f8d1740c96c68b6ace0086b39074f08ab89a"}, + {file = "tokenizers-0.19.1-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:bb9dfe7dae85bc6119d705a76dc068c062b8b575abe3595e3c6276480e67e3f1"}, + {file = "tokenizers-0.19.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:1f0360cbea28ea99944ac089c00de7b2e3e1c58f479fb8613b6d8d511ce98267"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:71e3ec71f0e78780851fef28c2a9babe20270404c921b756d7c532d280349214"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b82931fa619dbad979c0ee8e54dd5278acc418209cc897e42fac041f5366d626"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e8ff5b90eabdcdaa19af697885f70fe0b714ce16709cf43d4952f1f85299e73a"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e742d76ad84acbdb1a8e4694f915fe59ff6edc381c97d6dfdd054954e3478ad4"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8c5d59d7b59885eab559d5bc082b2985555a54cda04dda4c65528d90ad252ad"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b2da5c32ed869bebd990c9420df49813709e953674c0722ff471a116d97b22d"}, + {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:638e43936cc8b2cbb9f9d8dde0fe5e7e30766a3318d2342999ae27f68fdc9bd6"}, + {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:78e769eb3b2c79687d9cb0f89ef77223e8e279b75c0a968e637ca7043a84463f"}, + {file = "tokenizers-0.19.1-cp37-none-win32.whl", hash = "sha256:72791f9bb1ca78e3ae525d4782e85272c63faaef9940d92142aa3eb79f3407a3"}, + {file = "tokenizers-0.19.1-cp37-none-win_amd64.whl", hash = "sha256:f3bbb7a0c5fcb692950b041ae11067ac54826204318922da754f908d95619fbc"}, + {file = "tokenizers-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:07f9295349bbbcedae8cefdbcfa7f686aa420be8aca5d4f7d1ae6016c128c0c5"}, + {file = "tokenizers-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:10a707cc6c4b6b183ec5dbfc5c34f3064e18cf62b4a938cb41699e33a99e03c1"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6309271f57b397aa0aff0cbbe632ca9d70430839ca3178bf0f06f825924eca22"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ad23d37d68cf00d54af184586d79b84075ada495e7c5c0f601f051b162112dc"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:427c4f0f3df9109314d4f75b8d1f65d9477033e67ffaec4bca53293d3aca286d"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e83a31c9cf181a0a3ef0abad2b5f6b43399faf5da7e696196ddd110d332519ee"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c27b99889bd58b7e301468c0838c5ed75e60c66df0d4db80c08f43462f82e0d3"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bac0b0eb952412b0b196ca7a40e7dce4ed6f6926489313414010f2e6b9ec2adf"}, + {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8a6298bde623725ca31c9035a04bf2ef63208d266acd2bed8c2cb7d2b7d53ce6"}, + {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:08a44864e42fa6d7d76d7be4bec62c9982f6f6248b4aa42f7302aa01e0abfd26"}, + {file = "tokenizers-0.19.1-cp38-none-win32.whl", hash = "sha256:1de5bc8652252d9357a666e609cb1453d4f8e160eb1fb2830ee369dd658e8975"}, + {file = "tokenizers-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:0bcce02bf1ad9882345b34d5bd25ed4949a480cf0e656bbd468f4d8986f7a3f1"}, + {file = "tokenizers-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0b9394bd204842a2a1fd37fe29935353742be4a3460b6ccbaefa93f58a8df43d"}, + {file = "tokenizers-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4692ab92f91b87769d950ca14dbb61f8a9ef36a62f94bad6c82cc84a51f76f6a"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6258c2ef6f06259f70a682491c78561d492e885adeaf9f64f5389f78aa49a051"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c85cf76561fbd01e0d9ea2d1cbe711a65400092bc52b5242b16cfd22e51f0c58"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:670b802d4d82bbbb832ddb0d41df7015b3e549714c0e77f9bed3e74d42400fbe"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85aa3ab4b03d5e99fdd31660872249df5e855334b6c333e0bc13032ff4469c4a"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbf001afbbed111a79ca47d75941e9e5361297a87d186cbfc11ed45e30b5daba"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c89aa46c269e4e70c4d4f9d6bc644fcc39bb409cb2a81227923404dd6f5227"}, + {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:39c1ec76ea1027438fafe16ecb0fb84795e62e9d643444c1090179e63808c69d"}, + {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c2a0d47a89b48d7daa241e004e71fb5a50533718897a4cd6235cb846d511a478"}, + {file = "tokenizers-0.19.1-cp39-none-win32.whl", hash = "sha256:61b7fe8886f2e104d4caf9218b157b106207e0f2a4905c9c7ac98890688aabeb"}, + {file = "tokenizers-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:f97660f6c43efd3e0bfd3f2e3e5615bf215680bad6ee3d469df6454b8c6e8256"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3b11853f17b54c2fe47742c56d8a33bf49ce31caf531e87ac0d7d13d327c9334"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d26194ef6c13302f446d39972aaa36a1dda6450bc8949f5eb4c27f51191375bd"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e8d1ed93beda54bbd6131a2cb363a576eac746d5c26ba5b7556bc6f964425594"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca407133536f19bdec44b3da117ef0d12e43f6d4b56ac4c765f37eca501c7bda"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce05fde79d2bc2e46ac08aacbc142bead21614d937aac950be88dc79f9db9022"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:35583cd46d16f07c054efd18b5d46af4a2f070a2dd0a47914e66f3ff5efb2b1e"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:43350270bfc16b06ad3f6f07eab21f089adb835544417afda0f83256a8bf8b75"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b4399b59d1af5645bcee2072a463318114c39b8547437a7c2d6a186a1b5a0e2d"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6852c5b2a853b8b0ddc5993cd4f33bfffdca4fcc5d52f89dd4b8eada99379285"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd266ae85c3d39df2f7e7d0e07f6c41a55e9a3123bb11f854412952deacd828"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecb2651956eea2aa0a2d099434134b1b68f1c31f9a5084d6d53f08ed43d45ff2"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:b279ab506ec4445166ac476fb4d3cc383accde1ea152998509a94d82547c8e2a"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:89183e55fb86e61d848ff83753f64cded119f5d6e1f553d14ffee3700d0a4a49"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2edbc75744235eea94d595a8b70fe279dd42f3296f76d5a86dde1d46e35f574"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0e64bfde9a723274e9a71630c3e9494ed7b4c0f76a1faacf7fe294cd26f7ae7c"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b5ca92bfa717759c052e345770792d02d1f43b06f9e790ca0a1db62838816f3"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f8a20266e695ec9d7a946a019c1d5ca4eddb6613d4f466888eee04f16eedb85"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63c38f45d8f2a2ec0f3a20073cccb335b9f99f73b3c69483cd52ebc75369d8a1"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dd26e3afe8a7b61422df3176e06664503d3f5973b94f45d5c45987e1cb711876"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:eddd5783a4a6309ce23432353cdb36220e25cbb779bfa9122320666508b44b88"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:56ae39d4036b753994476a1b935584071093b55c7a72e3b8288e68c313ca26e7"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f9939ca7e58c2758c01b40324a59c034ce0cebad18e0d4563a9b1beab3018243"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c330c0eb815d212893c67a032e9dc1b38a803eccb32f3e8172c19cc69fbb439"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec11802450a2487cdf0e634b750a04cbdc1c4d066b97d94ce7dd2cb51ebb325b"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b718f316b596f36e1dae097a7d5b91fc5b85e90bf08b01ff139bd8953b25af"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ed69af290c2b65169f0ba9034d1dc39a5db9459b32f1dd8b5f3f32a3fcf06eab"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f8a9c828277133af13f3859d1b6bf1c3cb6e9e1637df0e45312e6b7c2e622b1f"}, + {file = "tokenizers-0.19.1.tar.gz", hash = "sha256:ee59e6680ed0fdbe6b724cf38bd70400a0c1dd623b07ac729087270caeac88e3"}, +] + +[package.dependencies] +huggingface-hub = ">=0.16.4,<1.0" + +[package.extras] +dev = ["tokenizers[testing]"] +docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] + +[[package]] +name = "tomli" +version = "1.2.3" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, + {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, +] + +[[package]] +name = "tqdm" +version = "4.66.2" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"}, + {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "typer" +version = "0.12.3" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "xlrd" +version = "2.0.1" +description = "Library for developers to extract data from Microsoft Excel (tm) .xls spreadsheet files" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "xlrd-2.0.1-py2.py3-none-any.whl", hash = "sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd"}, + {file = "xlrd-2.0.1.tar.gz", hash = "sha256:f72f148f54442c6b056bf931dbc34f986fd0c3b0b6b5a58d013c9aef274d0c88"}, +] + +[package.extras] +build = ["twine", "wheel"] +docs = ["sphinx"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "yarl" +version = "1.9.4" +description = "Yet another URL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[[package]] +name = "zipp" +version = "3.18.1" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, + {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "462340b43d9b25819bc82a2794d9e748ca10940da005c19eafb20df4dfcff316" diff --git a/pyproject.toml b/pyproject.toml index 3f5aeb5..195a39d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ xlrd = "^2.0.1" pypdfium2 = "^4.29.0" instructor = "^1.2.2" python-dotenv = "^1.0.1" +cachetools = "^5.3.3" [tool.poetry.dev-dependencies] flake8 = "^3.9.2" diff --git a/tests/classify.py b/tests/classify.py new file mode 100644 index 0000000..bc3aa0e --- /dev/null +++ b/tests/classify.py @@ -0,0 +1,31 @@ +import os +from dotenv import load_dotenv +from extract_thinker.extractor import Extractor +from extract_thinker.document_loader.document_loader_tesseract import DocumentLoaderTesseract +from extract_thinker.models import Classification, ClassificationResponse + +load_dotenv() +cwd = os.getcwd() + + +def test_classify_feature(): + # Arrange + tesseract_path = os.getenv("TESSERACT_PATH") + test_file_path = os.path.join(cwd, "test_images", "invoice.png") + + classifications = [ + Classification(name="Driver License", description="This is a driver license"), + Classification(name="Invoice", description="This is an invoice"), + ] + + extractor = Extractor() + extractor.load_document_loader(DocumentLoaderTesseract(tesseract_path)) + extractor.load_llm("claude-3-haiku-20240307") + + # Act + result = extractor.classify_from_path(test_file_path, classifications) + + # Assert + assert result is not None + assert isinstance(result, ClassificationResponse) + assert result.name == "Invoice" diff --git a/tests/document_loader_tesseract.py b/tests/document_loader_tesseract.py index 159e262..1e5ba13 100644 --- a/tests/document_loader_tesseract.py +++ b/tests/document_loader_tesseract.py @@ -36,3 +36,12 @@ def test_load_content_from_stream(): assert content is not None assert "Invoice" in content assert "0000001" in content + + +def test_cache_for_file(): + # Act + content1 = loader.load_content_from_file(test_file_path) + content2 = loader.load_content_from_file(test_file_path) + + # Assert + assert content1 is content2 diff --git a/tests/test_document_loader.py b/tests/test_document_loader.py deleted file mode 100644 index e69de29..0000000 From d40b6db9ac8803bce18422741fcca31f3f1caba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Almeida?= Date: Tue, 14 May 2024 15:53:02 +0100 Subject: [PATCH 3/7] 0.0.1v complete! Extract and Splitting working in basic usage. Tesseract working only in this version --- .github/workflows/workflow.yml | 8 +- README.md | 134 +++++++++----- examples/extractor_basic.py | 33 ++++ extract_thinker/__init__.py | 13 ++ extract_thinker/app.py | 29 ++- .../document_loader/document_loader.py | 66 +++++-- .../document_loader_tesseract.py | 38 +++- extract_thinker/extractor.py | 54 ++++-- extract_thinker/featurelog.txt | 1 - extract_thinker/image_splitter.py | 51 ++++-- extract_thinker/llm.py | 7 +- extract_thinker/models.py | 37 ---- extract_thinker/models/classification.py | 10 + .../models/classification_response.py | 8 + extract_thinker/models/contract.py | 5 + extract_thinker/models/doc_group.py | 12 ++ extract_thinker/models/doc_groups.py | 9 + extract_thinker/models/doc_groups2.py | 17 ++ extract_thinker/process.py | 173 +++++++++++++++--- extract_thinker/splitter.py | 51 +++--- extract_thinker/utils.py | 80 ++++++++ poetry.lock | 2 +- pyproject.toml | 4 +- tests/classify.py | 44 ++++- tests/models/driver_license.py | 7 + tests/models/invoice.py | 2 +- 26 files changed, 691 insertions(+), 204 deletions(-) create mode 100644 examples/extractor_basic.py delete mode 100644 extract_thinker/featurelog.txt delete mode 100644 extract_thinker/models.py create mode 100644 extract_thinker/models/classification.py create mode 100644 extract_thinker/models/classification_response.py create mode 100644 extract_thinker/models/contract.py create mode 100644 extract_thinker/models/doc_group.py create mode 100644 extract_thinker/models/doc_groups.py create mode 100644 extract_thinker/models/doc_groups2.py create mode 100644 tests/models/driver_license.py diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 31c5eaa..6cfcf37 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -11,7 +11,6 @@ on: jobs: build-and-test: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v3 - name: Set up Python @@ -29,3 +28,10 @@ jobs: - name: Build package run: poetry build + + - name: Publish to PyPI + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: pypa/gh-action-pypi-publish@v1.4.2 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/README.md b/README.md index 27d17d6..bc2ccd2 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,111 @@ -# Open-DocLLM +# ExtractThinker -## Introduction -This project aims to tackle the challenges of data extraction and processing using OCR and LLM. It is inspired by JP Morgan's DocLLM but is fully open-source and offers a larger context window size. The project is divided into two parts: the OCR and LLM layer. +Library to extract data from files and documents agnostically using LLMs. `extract_thinker` provides ORM-style interaction between files and LLMs, allowing for flexible and powerful document extraction workflows. -![image](https://github.com/enoch3712/Open-DocLLM/assets/9283394/2612cc9e-fc66-401e-912d-3acaef42d9cc) +## Features -## OCR Layer -The OCR layer is responsible for reading all the content from a document. It involves the following steps: +- Supports multiple document loaders including Tesseract OCR, Azure Form Recognizer, AWS TextExtract, Google Document AI. +- Customizable extraction using contract definitions. +- Asynchronous processing for efficient document handling. +- Built-in support for various document formats. +- ORM-style interaction between files and LLMs. -1. **Convert pages to images**: Any type of file is converted into an image so that all the content in the document can be read. +## Installation -2. **Preprocess image for OCR**: The image is adjusted to improve its quality and readability. +To install `extract_thinker`, you can use `pip`: -3. **Tesseract OCR**: The Tesseract OCR, the most popular open-source OCR in the world, is used to read the content from the images. +```bash +pip install extract_thinker +``` -## LLM Layer -The LLM layer is responsible for extracting specific content from the document in a structured way. It involves defining an extraction contract and extracting the JSON data. +## Usage +Here's a quick example to get you started with extract_thinker. This example demonstrates how to load a document using Tesseract OCR and extract specific fields defined in a contract. -## Running Locally -You can run the models on-premises using LLM studio or Ollama. This project uses LlamaIndex and Ollama. +```python +import os +from dotenv import load_dotenv +from extract_thinker import DocumentLoaderTesseract, Extractor, Contract -## Running the Code -The repo includes a FastAPI app with one endpoint for testing. Make sure to point to the proper Tesseract executable and change the key in the config.py file. +load_dotenv() +cwd = os.getcwd() -1. Install Tessaract -https://github.com/tesseract-ocr/tesseract +class InvoiceContract(Contract): + invoice_number: str + invoice_date: str -2. Install the required Python packages. -```sh -pip install -r requirements.txt -``` +# Arrange +tesseract_path = os.getenv("TESSERACT_PATH") +test_file_path = os.path.join(cwd, "test_images", "invoice.png") -3. Run fast api -```sh -uvicorn main:app --reload -``` +extractor = Extractor() +extractor.load_document_loader( + DocumentLoaderTesseract(tesseract_path) +) +extractor.load_llm("claude-3-haiku-20240307") -4. go to the Swgger page: -http://localhost:8000/docs +# Act +result = extractor.extract(test_file_path, InvoiceContract) -## Running with Docker -1. Build the Docker image. -```sh -docker build -t your-image-name . +# Assert +assert result is not None +assert result.invoice_number == "0000001" +assert result.invoice_date == "2014-05-07" ``` -2. Run the Docker container. -```sh -docker run -p 8000:8000 your-image-name -``` +## Splitting Files Example +You can also split and process documents using extract_thinker. Here's how you can do it: + +```python +import os +from dotenv import load_dotenv +from extract_thinker import DocumentLoaderTesseract, Extractor, Process, Classification, ImageSplitter + +load_dotenv() + +class DriverLicense(Contract): + # Define your DriverLicense contract fields here + pass -3. go to the Swgger page: -http://localhost:8000/docs +class InvoiceContract(Contract): + invoice_number: str + invoice_date: str + +extractor = Extractor() +extractor.load_document_loader(DocumentLoaderTesseract(os.getenv("TESSERACT_PATH"))) +extractor.load_llm("gpt-3.5-turbo") + +classifications = [ + Classification(name="Driver License", description="This is a driver license", contract=DriverLicense, extractor=extractor), + Classification(name="Invoice", description="This is an invoice", contract=InvoiceContract, extractor=extractor) +] + +process = Process() +process.load_document_loader(DocumentLoaderTesseract(os.getenv("TESSERACT_PATH"))) +process.load_splitter(ImageSplitter()) + +path = "C:\\Users\\Lopez\\Desktop\\MagniFinance\\examples\\outputTestOne.pdf" +other_path = "C:\\Users\\Lopez\\Desktop\\MagniFinance\\examples\\SingleInvoiceTests\\FT63O.pdf" + +split_content = process.load_file(path)\ + .split(classifications)\ + .extract() + +# Process the split_content as needed +``` +## Additional Examples +You can find more examples in the repository. These examples cover various use cases and demonstrate the flexibility of extract_thinker. -## Advanced Cases: 1 Million token context -The project also explores advanced cases like a 1 million token context using LLM Lingua and Mistral Yarn 128k context window. +## Contributing +We welcome contributions from the community! If you would like to contribute, please follow these steps: -## Conclusion -The integration of OCR and LLM technologies in this project marks a pivotal advancement in analyzing unstructured data. The combination of open-source projects like Tesseract and Mistral makes a perfect implementation that could be used in an on-premise use case. +Fork the repository. +Create a new branch for your feature or bugfix. +Write tests for your changes. +Run tests to ensure everything is working correctly. +Submit a pull request with a description of your changes. +License +This project is licensed under the Apache License 2.0. See the LICENSE file for more details. -## References & Documents  -1. [DOCLLM: A LAYOUT-AWARE GENERATIVE LANGUAGE MODEL FOR MULTIMODAL DOCUMENT UNDERSTANDING](https://arxiv.org/pdf/2401.00908.pdf) -2. [YaRN: Efficient Context Window Extension of Large Language Models](https://arxiv.org/pdf/2309.00071.pdf) \ No newline at end of file +## Contact +For any questions or issues, please open an issue on the GitHub repository. \ No newline at end of file diff --git a/examples/extractor_basic.py b/examples/extractor_basic.py new file mode 100644 index 0000000..28f9088 --- /dev/null +++ b/examples/extractor_basic.py @@ -0,0 +1,33 @@ +import os + +from dotenv import load_dotenv + +from extract_thinker import DocumentLoaderTesseract, Extractor, Contract + +load_dotenv() +cwd = os.getcwd() + + +class InvoiceContract(Contract): + invoice_number: str + invoice_date: str + + +tesseract_path = os.getenv("TESSERACT_PATH") +test_file_path = os.path.join(cwd, "tests", "test_images", "invoice.png") + +extractor = Extractor() +extractor.load_document_loader( + DocumentLoaderTesseract(tesseract_path) +) +extractor.load_llm("claude-3-haiku-20240307") + +result = extractor.extract(test_file_path, InvoiceContract) + +if result is not None: + print("Extraction successful.") +else: + print("Extraction failed.") + +print("Invoice Number: ", result.invoice_number) +print("Invoice Date: ", result.invoice_date) diff --git a/extract_thinker/__init__.py b/extract_thinker/__init__.py index e69de29..84b277d 100644 --- a/extract_thinker/__init__.py +++ b/extract_thinker/__init__.py @@ -0,0 +1,13 @@ +from .extractor import Extractor +from .document_loader.document_loader import DocumentLoader +from .document_loader.cached_document_loader import CachedDocumentLoader +from .document_loader.document_loader_tesseract import DocumentLoaderTesseract +from .models import classification, classification_response +from .process import Process +from .splitter import Splitter +from .image_splitter import ImageSplitter +from .models.classification import Classification +from .models.contract import Contract + + +__all__ = ['Extractor', 'DocumentLoader', 'CachedDocumentLoader', 'DocumentLoaderTesseract', 'classification', 'classification_response', 'Process', 'Splitter', 'ImageSplitter', 'Classification', 'Contract'] diff --git a/extract_thinker/app.py b/extract_thinker/app.py index a4d6bb1..18494ba 100644 --- a/extract_thinker/app.py +++ b/extract_thinker/app.py @@ -1,19 +1,36 @@ +import os from dotenv import load_dotenv from extract_thinker.document_loader.document_loader_tesseract import DocumentLoaderTesseract +from extract_thinker.process import Process +from extract_thinker.image_splitter import ImageSplitter from extractor import Extractor -from models import Classification - +from extract_thinker.models.classification import Classification +from tests.models.invoice import InvoiceContract +from tests.models.driver_license import DriverLicense load_dotenv() +# Usage +extractor = Extractor() +extractor.load_document_loader(DocumentLoaderTesseract(os.getenv("TESSERACT_PATH"))) +extractor.load_llm("gpt-3.5-turbo") + classifications = [ - Classification(name="Driver License", description="This is a driver license"), - Classification(name="Invoice", description="This is an invoice"), + Classification(name="Driver License", description="This is a driver license", contract=DriverLicense, extractor=extractor), + Classification(name="Invoice", description="This is an invoice", contract=InvoiceContract, extractor=extractor) ] -# Usage -extractor = Extractor() +process = Process() +process.load_document_loader(DocumentLoaderTesseract(os.getenv("TESSERACT_PATH"))) +process.load_splitter(ImageSplitter()) + +path = "C:\\Users\\Lopez\\Desktop\\MagniFinance\\examples\\outputTestOne.pdf" +other_path = "C:\\Users\\Lopez\\Desktop\\MagniFinance\\examples\\SingleInvoiceTests\\FT63O.pdf" + +split_content = process.load_file(path)\ + .split(classifications)\ + .extract() # extractor.loadSplitter(ImageSplitter()) # extractor.loadfile( diff --git a/extract_thinker/document_loader/document_loader.py b/extract_thinker/document_loader/document_loader.py index 901a7b3..aec7c1b 100644 --- a/extract_thinker/document_loader/document_loader.py +++ b/extract_thinker/document_loader/document_loader.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod +import io from PIL import Image from io import BytesIO import pypdfium2 as pdfium @@ -24,7 +25,32 @@ def load_content_from_stream(self, stream: BytesIO) -> Union[str, object]: def getContent(self) -> Any: return self.content - def convert_pdf_to_images(self, file_path: str, scale: float = 300 / 72) -> List[Dict[int, bytes]]: + def load_content_list(self, input_data: Union[str, BytesIO, List[Union[str, BytesIO]]]) -> Union[str, List[str]]: + if isinstance(input_data, (str, BytesIO)): + return self.load_content_from_stream_list(input_data) + elif isinstance(input_data, list): + return self.load_content_from_file_list(input_data) + else: + raise Exception(f"Unsupported input type: {type(input_data)}") + + @abstractmethod + def load_content_from_stream_list(self, stream: BytesIO) -> List[Any]: + pass + + @abstractmethod + def load_content_from_file_list(self, file_path: str) -> List[Any]: + pass + + def convert_to_images(self, file: Union[str, io.BytesIO], scale: float = 300 / 72) -> Dict[int, bytes]: + # Determine if the input is a file path or a stream + if isinstance(file, str): + return self._convert_file_to_images(file, scale) + elif isinstance(file, io.BytesIO): + return self._convert_stream_to_images(file, scale) + else: + raise TypeError("file must be a file path (str) or a BytesIO stream") + + def _convert_file_to_images(self, file_path: str, scale: float) -> Dict[int, bytes]: # Check if the file is already an image try: Image.open(file_path) @@ -35,22 +61,40 @@ def convert_pdf_to_images(self, file_path: str, scale: float = 300 / 72) -> List if is_image: # If it is, return it as is with open(file_path, "rb") as f: - return [{0: f.read()}] + return {0: f.read()} + + # If it's not an image, proceed with the conversion + return self._convert_pdf_to_images(pdfium.PdfDocument(file_path), scale) + + def _convert_stream_to_images(self, file_stream: io.BytesIO, scale: float) -> Dict[int, bytes]: + # Check if the stream is already an image + try: + Image.open(file_stream) + is_image = True + except IOError: + is_image = False + + # Reset stream position + file_stream.seek(0) + + if is_image: + # If it is, return it as is + return {0: file_stream.read()} # If it's not an image, proceed with the conversion - pdf_file = pdfium.PdfDocument(file_path) + # Note: pdfium.PdfDocument may not support streams directly. + # You might need to save the stream to a temporary file first. + return self._convert_pdf_to_images(pdfium.PdfDocument(file_stream), scale) + def _convert_pdf_to_images(self, pdf_file, scale: float) -> Dict[int, bytes]: page_indices = [i for i in range(len(pdf_file))] with concurrent.futures.ThreadPoolExecutor() as executor: - futures = [] - for i in page_indices: - future = executor.submit(self.render_page, pdf_file, i, scale) - futures.append(future) - - final_images = [] - for future in concurrent.futures.as_completed(futures): - final_images.append(future.result()) + futures = {i: executor.submit(self.render_page, pdf_file, i, scale) for i in page_indices} + + final_images = {} + for i, future in futures.items(): + final_images[i] = future.result() return final_images diff --git a/extract_thinker/document_loader/document_loader_tesseract.py b/extract_thinker/document_loader/document_loader_tesseract.py index 5f6d274..58a1b8f 100644 --- a/extract_thinker/document_loader/document_loader_tesseract.py +++ b/extract_thinker/document_loader/document_loader_tesseract.py @@ -1,15 +1,16 @@ from io import BytesIO from operator import attrgetter import os -from typing import Union +from typing import Any, List, Union from PIL import Image import pytesseract from extract_thinker.document_loader.cached_document_loader import CachedDocumentLoader -from ..utils import get_image_type +from extract_thinker.utils import get_image_type from cachetools import cachedmethod from cachetools.keys import hashkey +import concurrent.futures SUPPORTED_IMAGE_FORMATS = ["jpeg", "png", "bmp", "tiff"] @@ -52,3 +53,36 @@ def load_content_from_stream(self, stream: Union[BytesIO, str]) -> Union[str, ob raise Exception(f"Unsupported stream type: {stream}") except Exception as e: raise Exception(f"Error processing stream: {e}") from e + + def process_image(self, image): + for attempt in range(3): + raw_text = str(pytesseract.image_to_string(Image.open(BytesIO(image)))) + if raw_text: + return raw_text + raise Exception("Failed to process image after 3 attempts") + + @cachedmethod(cache=attrgetter('cache'), key=lambda self, stream: hashkey(id(stream))) + def load_content_from_stream_list(self, stream: BytesIO) -> List[Any]: + images = self.convert_to_images(stream) + + with concurrent.futures.ThreadPoolExecutor() as executor: + futures = {i: executor.submit(self.process_image, image[i]) for i, image in enumerate(images.values())} + + contents = [] + for i, future in futures.items(): + contents.append({"image": images[i], "content": future.result()}) + + return contents + + @cachedmethod(cache=attrgetter('cache'), key=lambda self, input_list: hashkey(id(input_list))) + def load_content_from_file_list(self, input: List[Union[str, BytesIO]]) -> List[Any]: + images = self.convert_to_images(input) + + with concurrent.futures.ThreadPoolExecutor() as executor: + futures = {i: executor.submit(self.process_image, image[i]) for i, image in enumerate(images.values())} + + contents = [] + for i, future in futures.items(): + contents.append({"image": Image.open(BytesIO(images[i][i])), "content": future.result()}) + + return contents \ No newline at end of file diff --git a/extract_thinker/extractor.py b/extract_thinker/extractor.py index 623afa9..c041768 100644 --- a/extract_thinker/extractor.py +++ b/extract_thinker/extractor.py @@ -1,16 +1,23 @@ -from typing import List, Optional, IO, Union +import asyncio +from typing import Any, Dict, List, Optional, IO, Union + +from pydantic import BaseModel from extract_thinker.document_loader.document_loader import DocumentLoader -from extract_thinker.models import ( - Classification, +from extract_thinker.models.classification import Classification +from extract_thinker.models.classification_response import ( ClassificationResponse, ) -from extract_thinker.splitter import Splitter from extract_thinker.llm import LLM import os from extract_thinker.document_loader.loader_interceptor import LoaderInterceptor from extract_thinker.document_loader.llm_interceptor import LlmInterceptor +from extract_thinker.utils import get_image_type + + +SUPPORTED_IMAGE_FORMATS = ["jpeg", "png", "bmp", "tiff"] + class Extractor: def __init__( @@ -18,9 +25,8 @@ def __init__( ): self.document_loader: Optional[DocumentLoader] = processor self.llm: Optional[LLM] = llm - self.splitter: Optional[Splitter] = None self.file: Optional[str] = None - self.document_loaders_by_file_type = {} + self.document_loaders_by_file_type: Dict[str, DocumentLoader] = {} self.loader_interceptors: List[LoaderInterceptor] = [] self.llm_interceptors: List[LlmInterceptor] = [] @@ -51,15 +57,29 @@ def load_document_loader(self, document_loader: DocumentLoader) -> None: def load_llm(self, model: str) -> None: self.llm = LLM(model) - def extract( - self, source: Union[str, IO], response_model: str, vision: bool = False - ) -> str: + def extract(self, source: Union[str, IO, list], response_model: type[BaseModel], vision: bool = False) -> str: + if not issubclass(response_model, BaseModel): + raise ValueError("response_model must be a subclass of Pydantic's BaseModel.") + if isinstance(source, str): # if it's a file path return self.extract_from_file(source, response_model, vision) elif isinstance(source, IO): # if it's a stream return self.extract_from_stream(source, response_model, vision) + elif isinstance(source, list) and all(isinstance(item, dict) for item in source): # if it's a list of dictionaries + return self.extract_from_list(source, response_model, vision) else: - raise ValueError("Source must be a file path or a stream") + raise ValueError("Source must be a file path, a stream, or a list of dictionaries") + + async def extract_async(self, source: Union[str, IO, list], response_model: type[BaseModel], vision: bool = False) -> str: + return await asyncio.to_thread(self.extract, source, response_model, vision) + + def extract_from_list(self, data: List[Dict[Any, Any]], response_model: type[BaseModel], vision: bool) -> str: + # check if document_loader is None, raise error + if self.document_loader is None: + raise ValueError("Document loader is not set") + + content = "\n".join([f"#{k}:\n{v}" for d in data for k, v in d.items() if k != "image"]) + return self._extract(content, data, response_model, vision, is_stream=False) def extract_from_file( self, file: str, response_model: str, vision: bool = False @@ -112,20 +132,20 @@ def _classify(self, content: str, classifications: List[Classification]): return response - def classify(self, input: Union[str, IO]): + def classify(self, input: Union[str, IO], classifications: List[Classification]): if isinstance(input, str): # Check if the input is a valid file path if os.path.isfile(input): - _, ext = os.path.splitext(input) - if ext.lower() == ".pdf": - return self.classify_from_path(input) + file_type = get_image_type(input) + if file_type in SUPPORTED_IMAGE_FORMATS: + return self.classify_from_path(input, classifications) else: - raise ValueError(f"Unsupported file type: {ext}") + raise ValueError(f"Unsupported file type: {input}") else: raise ValueError(f"No such file: {input}") elif hasattr(input, 'read'): # Check if the input is a stream (like a file object) - return self.classify_from_stream(input) + return self.classify_from_stream(input, classifications) else: raise ValueError("Input must be a file path or a stream.") @@ -164,7 +184,7 @@ def _extract( } ] else: - messages.append({"role": "user", "content": content}) + messages.append({"role": "user", "content": "##Content\n\n" + content}) response = self.llm.request(messages, response_model) return response diff --git a/extract_thinker/featurelog.txt b/extract_thinker/featurelog.txt deleted file mode 100644 index 0fc6113..0000000 --- a/extract_thinker/featurelog.txt +++ /dev/null @@ -1 +0,0 @@ -Add semantic search for the different languages and files diff --git a/extract_thinker/image_splitter.py b/extract_thinker/image_splitter.py index 860cc4d..3775cbb 100644 --- a/extract_thinker/image_splitter.py +++ b/extract_thinker/image_splitter.py @@ -1,32 +1,37 @@ -from ExtractThinker.models import Classification, DocGroups2 -from ExtractThinker.utils import encode_image -from splitter import Splitter - +import base64 import litellm -from PIL import Image +from io import BytesIO +from typing import List, Any +from extract_thinker.models.classification import Classification +from extract_thinker.models.doc_groups2 import DocGroups2 +from extract_thinker.splitter import Splitter +from extract_thinker.utils import extract_json -from typing import IO, List, Union +class ImageSplitter(Splitter): + def encode_image(self, image): + buffered = BytesIO() + image.save(buffered, format=image.format) + img_byte = buffered.getvalue() + return base64.b64encode(img_byte).decode("utf-8") -class ImageSplitter(Splitter): def belongs_to_same_document(self, - page1: Union[str, IO], - page2: Union[str, IO], + obj1: Any, + obj2: Any, classifications: List[Classification] ) -> DocGroups2: - assistantPrompt = 'What you are an API that extracts information. You receive as input: \r\n1. two pages \r\n2. a group of classifications\r\n output:\r\nA JSON with the classification of each document and if belongs to the same document\r\n\r\n//Example 1\r\n//can be null if belongsToSamePage is true\r\n{\r\n "belongsToSameDocument": true,\r\n "classificationPage1": "LLC",\r\n "classificationPage2": "LLC"\r\n}\r\n//Example 2\r\n{\r\n "belongsToSameDocument": false,\r\n "classificationPage1": "LLC",\r\n "classificationPage2": "Invoice"\r\n}' + if 'image' not in obj1 or 'image' not in obj2: + raise ValueError("Input objects must have an 'image' key") + + page1 = obj1['image'] + page2 = obj2['image'] - # make sure image1 and image2 are images and not text - try: - Image.open(page1) - Image.open(page2) - except IOError: - return {"error": "One or both of the input pages are not valid images."} + assistantPrompt = 'What you are an API that extracts information. You receive as input: \r\n1. two pages \r\n2. a group of classifications\r\n output:\r\nA JSON with the classification of each document and if belongs to the same document\r\n\r\n//Example 1\r\n//can be null if belongsToSamePage is true\r\n{\r\n "belongs_to_same_document": true,\r\n "classification_page1": "LLC",\r\n "classification_page2": "LLC"\r\n}\r\n//Example 2\r\n{\r\n "belongs_to_same_document": false,\r\n "classification_page1": "LLC",\r\n "classification_page2": "Invoice"\r\n}' - base64_image1 = encode_image(page1) - base64_image2 = encode_image(page2) + base64_image1 = self.encode_image(page1) + base64_image2 = self.encode_image(page2) classifications_text = ( "##Classifications\n" @@ -57,9 +62,17 @@ def belongs_to_same_document(self, "url": "data:image/jpeg;base64," + base64_image2 }, }, + {"type": "text", "text": "###JSON Output\n"}, ], }, ], ) - return resp + jsonText = resp.choices[0].message.content + + jsonText = extract_json(jsonText) + + # TODO: eventually will be done in a more robust way + validated_obj = DocGroups2(**jsonText) + + return validated_obj diff --git a/extract_thinker/llm.py b/extract_thinker/llm.py index f65f826..f6470df 100644 --- a/extract_thinker/llm.py +++ b/extract_thinker/llm.py @@ -1,5 +1,6 @@ import instructor import litellm +from extract_thinker.utils import num_tokens_from_string class LLM: @@ -8,9 +9,13 @@ def __init__(self, model): self.model = model def request(self, messages, response_model): + + contents = map(lambda message: message['content'], messages) + + all_contents = ' '.join(contents) return self.client.chat.completions.create( model=self.model, - max_tokens=1024, + max_tokens=num_tokens_from_string(all_contents), messages=messages, response_model=response_model, ) diff --git a/extract_thinker/models.py b/extract_thinker/models.py deleted file mode 100644 index 71939f7..0000000 --- a/extract_thinker/models.py +++ /dev/null @@ -1,37 +0,0 @@ -from typing import List, Optional -from pydantic import BaseModel -from dataclasses import dataclass - - -class Contract(BaseModel): - pass - - -class Classification(BaseModel): - name: str - description: str - contract: Optional[Contract] = None - - -class ClassificationResponse(BaseModel): - name: str - - -@dataclass -class DocGroups2: - certainty: float - belongs_to_same_document: bool - classification_page1: str - classification_page2: str - - -class DocGroup: - def __init__(self): - self.pages: List[int] = [] - self.classification: str = "" - self.certainties: List[float] = [] - - -class DocGroups: - def __init__(self): - self.doc_groups: List[DocGroup] = [] diff --git a/extract_thinker/models/classification.py b/extract_thinker/models/classification.py new file mode 100644 index 0000000..46b39d9 --- /dev/null +++ b/extract_thinker/models/classification.py @@ -0,0 +1,10 @@ +from typing import Any, Optional +from extract_thinker.models.contract import Contract +from pydantic import BaseModel + + +class Classification(BaseModel): + name: str + description: str + contract: type[Contract] + extractor: Optional[Any] = None diff --git a/extract_thinker/models/classification_response.py b/extract_thinker/models/classification_response.py new file mode 100644 index 0000000..8b75e0d --- /dev/null +++ b/extract_thinker/models/classification_response.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel + + +class ClassificationResponse(BaseModel): + name: str + + def __hash__(self): + return hash((self.name)) diff --git a/extract_thinker/models/contract.py b/extract_thinker/models/contract.py new file mode 100644 index 0000000..11ad79e --- /dev/null +++ b/extract_thinker/models/contract.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class Contract(BaseModel): + pass diff --git a/extract_thinker/models/doc_group.py b/extract_thinker/models/doc_group.py new file mode 100644 index 0000000..8fe9071 --- /dev/null +++ b/extract_thinker/models/doc_group.py @@ -0,0 +1,12 @@ +from typing import List + + +class DocGroup: + def __init__(self, pages: List[int], classification: str): + self.pages = pages + self.classification = classification + + +class DocGroups: + def __init__(self): + self.doc_groups: List[DocGroup] = [] \ No newline at end of file diff --git a/extract_thinker/models/doc_groups.py b/extract_thinker/models/doc_groups.py new file mode 100644 index 0000000..d17d515 --- /dev/null +++ b/extract_thinker/models/doc_groups.py @@ -0,0 +1,9 @@ +from extract_thinker.models.doc_group import DocGroup + + +from typing import List + + +class DocGroups: + def __init__(self): + self.doc_groups: List[DocGroup] = [] diff --git a/extract_thinker/models/doc_groups2.py b/extract_thinker/models/doc_groups2.py new file mode 100644 index 0000000..1e16874 --- /dev/null +++ b/extract_thinker/models/doc_groups2.py @@ -0,0 +1,17 @@ +from dataclasses import dataclass + + +@dataclass +class DocGroups2: + # certainty: float + belongs_to_same_document: bool + classification_page1: str + classification_page2: str + + def __eq__(self, other): + if not isinstance(other, DocGroups2): + return NotImplemented + return id(self) == id(other) + + def __hash__(self): + return id(self) diff --git a/extract_thinker/process.py b/extract_thinker/process.py index 6b44f14..d51f731 100644 --- a/extract_thinker/process.py +++ b/extract_thinker/process.py @@ -1,51 +1,118 @@ import asyncio -from typing import List, Optional +from typing import IO, Any, Dict, List, Optional from extract_thinker.extractor import Extractor -from extract_thinker.models import Classification -from extract_thinker.models import ( - DocGroups2, - DocGroup, +from extract_thinker.models.classification import Classification +from extract_thinker.document_loader.document_loader import DocumentLoader +from extract_thinker.models.doc_group import DocGroup +from extract_thinker.models.doc_groups2 import DocGroups2 +from extract_thinker.splitter import Splitter +from extract_thinker.models.doc_groups import ( DocGroups, ) -import os +from extract_thinker.utils import get_image_type class Process: def __init__(self): - self.extractors: List[Extractor] = [] - doc_groups: Optional[DocGroups] = None - - def add_extractor(self, extractor: Extractor): - self.extractors.append(extractor) + # self.extractors: List[Extractor] = [] + self.doc_groups: Optional[DocGroups] = None + self.split_classifications: List[Classification] = [] + self.extractor_groups: List[List[Extractor]] = [] # for classication + self.document_loaders_by_file_type: Dict[str, DocumentLoader] = {} + self.document_loader: Optional[DocumentLoader] = None + self.file_path: Optional[str] = None + self.file_stream: Optional[IO] = None + self.splitter: Optional[Splitter] = None + + def set_document_loader_for_file_type(self, file_type: str, document_loader: DocumentLoader): + if self.document_loader is not None: + raise ValueError("Cannot set a document loader for a specific file type when a default loader is already set.") + self.document_loaders_by_file_type[file_type] = document_loader + + def load_document_loader(self, document_loader: DocumentLoader): + if self.document_loaders_by_file_type: + raise ValueError("Cannot set a default document loader when specific loaders are already set.") + self.document_loader = document_loader + return self - def loadSplitter(self, splitter): + def load_splitter(self, splitter: Splitter): self.splitter = splitter return self + def add_classifyExtractor(self, extractor_groups: List[List[Extractor]]): + for extractors in extractor_groups: + self.extractor_groups.append(extractors) + return self + + async def _classify_async(self, extractor, file, classifications): + loop = asyncio.get_event_loop() + return await loop.run_in_executor(None, extractor.classify, file, classifications) + + async def classify_async(self, file: str, classifications) -> Optional[Classification]: + for extractor_group in self.extractor_groups: + group_classifications = await asyncio.gather(*(self._classify_async(extractor, file, classifications) for extractor in extractor_group)) + + # Check if all classifications in the group are the same + if len(set(group_classifications)) == 1: + return group_classifications[0] + + # If no agreement was found, return None + return None + + async def classify_extractor(self, session, extractor, file): + return await session.run(extractor.classify, file) + + # check if there is only the default one, if not, get from the file type. if none is present, raise an error + def get_document_loader(self, file): + if self.document_loader is not None: + return self.document_loader + + filetype = get_image_type(file) + return self.document_loaders_by_file_type.get(filetype, None) + + def load_file(self, file): + self.file_path = file + return self + def split(self, classifications: List[Classification]): - splitter = self.splitter - # Check if the file is a PDF - _, ext = os.path.splitext(self.file) - if ext.lower() != ".pdf": - raise ValueError("Invalid file type. Only PDFs are accepted.") + self.split_classifications = classifications + + documentLoader = self.get_document_loader(self.file_path) + + if documentLoader is None: + raise ValueError("No suitable document loader found for file type") + + if self.file_path: + content = documentLoader.load_content_from_file_list(self.file_path) + elif self.file_stream: + content = documentLoader.load_content_from_stream_list(self.file_stream) + else: + raise ValueError("No file or stream available") - images = self.document_loader.convert_pdf_to_images(self.file) + if len(content) == 1: + raise ValueError("Document must have at least 2 pages") - groups = splitter.split_document_into_groups([self.file]) + groups = self.splitter.split_document_into_groups(content) loop = asyncio.get_event_loop() processedGroups = loop.run_until_complete( - splitter.process_split_groups(groups, classifications) + self.splitter.process_split_groups(groups, classifications) ) doc_groups = self.aggregate_split_documents_2(processedGroups) + # doc_groups = DocGroups() + # doc_groups.doc_groups.append(DocGroup(pages=[1], classification='Invoice')) + # doc_groups.doc_groups.append(DocGroup(pages=[2], classification='Invoice')) + # doc_groups.doc_groups.append(DocGroup(pages=[3], classification='Invoice')) + # doc_groups.doc_groups.append(DocGroup(pages=[4, 5], classification='Invoice')) + self.doc_groups = doc_groups return self - def aggregate_split_documents_2(doc_groups_tasks: List[DocGroups2]) -> DocGroups: + def aggregate_split_documents_2(self, doc_groups_tasks: List[DocGroups2]) -> DocGroups: doc_groups = DocGroups() current_group = DocGroup() page_number = 1 @@ -56,21 +123,15 @@ def aggregate_split_documents_2(doc_groups_tasks: List[DocGroups2]) -> DocGroups if doc_group.belongs_to_same_document: current_group.pages = [1, 2] current_group.classification = doc_group.classification_page1 - current_group.certainties = [ - doc_group.certainty, - doc_groups_tasks[1].certainty, - ] else: current_group.pages = [1] current_group.classification = doc_group.classification_page1 - current_group.certainties = [doc_group.certainty] doc_groups.doc_groups.append(current_group) current_group = DocGroup() current_group.pages = [2] current_group.classification = doc_group.classification_page2 - current_group.certainties = [doc_groups_tasks[1].certainty] page_number += 1 @@ -79,14 +140,12 @@ def aggregate_split_documents_2(doc_groups_tasks: List[DocGroups2]) -> DocGroups if doc_group_2.belongs_to_same_document: current_group.pages.append(page_number + 1) - current_group.certainties.append(doc_group_2.certainty) else: doc_groups.doc_groups.append(current_group) current_group = DocGroup() current_group.classification = doc_group_2.classification_page2 current_group.pages = [page_number + 1] - current_group.certainties = [doc_group_2.certainty] page_number += 1 @@ -96,3 +155,59 @@ def aggregate_split_documents_2(doc_groups_tasks: List[DocGroups2]) -> DocGroups def where(self, condition): pass + + def extract(self) -> List[Any]: + if self.doc_groups is None: + raise ValueError("Document groups have not been initialized") + + async def _extract(doc_group): + classificationStr = doc_group.classification # str + + for classification in self.split_classifications: + if classification.name == classificationStr: + extractor = classification.extractor + contract = classification.contract + break + + if extractor is None: + raise ValueError("Extractor not found for classification") + + documentLoader = self.get_document_loader(self.file_path) + + if documentLoader is None: + raise ValueError("No suitable document loader found for file type") + + if self.file_path: + content = documentLoader.load_content_from_file_list(self.file_path) + elif self.file_stream: + content = documentLoader.load_content_from_stream_list(self.file_stream) + else: + raise ValueError("No file or stream available") + + # doc_groups contains e.g [1,2], [3], [4,5] and doc_group is e.g [1,2] + # content is a list of pages with the content of each page + # get the content of the pages, add them together and extract the data + + pages_content = [content[i - 1] for i in doc_group.pages] + return await extractor.extract_async(pages_content, contract) + + doc_groups = self.doc_groups.doc_groups + + async def process_doc_groups(groups: List[Any]) -> List[Any]: + # Create asynchronous tasks for processing each group + tasks = [_extract(group) for group in groups] + try: + # Execute all tasks concurrently and wait for all to complete + processedGroups = await asyncio.gather(*tasks) + return processedGroups + except Exception as e: + # Handle possible exceptions that might occur during task execution + print(f"An error occurred: {e}") + raise + + loop = asyncio.get_event_loop() + processedGroups = loop.run_until_complete( + process_doc_groups(doc_groups) + ) + + return processedGroups diff --git a/extract_thinker/splitter.py b/extract_thinker/splitter.py index 9a51af5..dd9858b 100644 --- a/extract_thinker/splitter.py +++ b/extract_thinker/splitter.py @@ -1,43 +1,36 @@ - import asyncio +from typing import Any, List from abc import ABC, abstractmethod -from typing import IO, List, Union -from extract_thinker.models import Classification, DocGroups2 +from extract_thinker.models.doc_groups2 import DocGroups2 class Splitter(ABC): @abstractmethod - def belongs_to_same_document(self, - page1: Union[str, IO], - page2: Union[str, IO], - contract: str) -> DocGroups2: + def belongs_to_same_document(self, page1: Any, page2: Any, contract: str) -> DocGroups2: pass - def split_document_into_groups( - self, document: List[Union[str, IO]] - ) -> List[List[Union[str, IO]]]: - # Assuming document is a list of pages + def split_document_into_groups(self, document: List[Any]) -> List[List[Any]]: page_per_split = 2 split = [] - for i in range(0, len(document), page_per_split): + if len(document) == 1: + return [document] + for i in range(0, len(document) - 1): group = document[i: i + page_per_split] - # If last group has only one page, remove it - if len(group) != 1: - split.append(group) + split.append(group) return split - async def process_split_groups(self, - split: List[List[Union[str, IO]]], - classifications: List[Classification] - ) -> List[DocGroups2]: - tasks = [self.process_group(x, classifications) for x in split] - doc_groups = await asyncio.gather(*tasks) - return doc_groups + async def process_split_groups(self, split: List[List[Any]], contract: str) -> List[DocGroups2]: + # Create asynchronous tasks for processing each group + tasks = [self.process_group(x, contract) for x in split] + try: + # Execute all tasks concurrently and wait for all to complete + doc_groups = await asyncio.gather(*tasks) + return doc_groups + except Exception as e: + # Handle possible exceptions that might occur during task execution + print(f"An error occurred: {e}") + raise - async def process_group(self, - group: List[Union[str, IO]], - contract: str) -> DocGroups2: - split_result = await self.belongs_to_same_document(group[0], - group[1], - contract) - return split_result + async def process_group(self, group: List[Any], contract: str) -> DocGroups2: + page2 = group[1] if len(group) > 1 else None + return self.belongs_to_same_document(group[0], page2, contract) diff --git a/extract_thinker/utils.py b/extract_thinker/utils.py index 3d6073f..bd537f4 100644 --- a/extract_thinker/utils.py +++ b/extract_thinker/utils.py @@ -1,5 +1,11 @@ import base64 +import json +import re +import yaml from PIL import Image +import tiktoken +from pydantic import BaseModel +import typing def encode_image(image_path): @@ -13,3 +19,77 @@ def get_image_type(image_path): return img.format.lower() except IOError as e: return f"An error occurred: {str(e)}" + + +def verify_json(json_content: str): + try: + data = json.loads(json_content) + return True, data, "JSON is valid." + except json.JSONDecodeError as e: + return False, None, f"Invalid JSON: {str(e)}" + + +def convert_json_to_yaml(json_data: str): + yaml_content = yaml.safe_dump(json_data, default_flow_style=False, sort_keys=False) + return yaml_content + + +def verify_yaml(yaml_content: str): + try: + data = yaml.safe_load(yaml_content) + return True, data, "YAML is valid." + except yaml.YAMLError as e: + return False, None, f"Invalid YAML: {str(e)}" + + +def convert_yaml_to_json(yaml_data: str): + json_content = json.dumps(yaml_data, indent=4) + return json_content + + +def num_tokens_from_string(text: str) -> int: + """ + Returns the number of tokens in a text string using the GPT-4 encoding. + + Args: + text (str): The text string to tokenize. + + Returns: + int: The number of tokens. + """ + # Automatically load the encoding for GPT-4 + encoding = tiktoken.encoding_for_model("gpt-4") + + # Encode the string to get the list of token integers. + tokens = encoding.encode(text) + + # Return the number of tokens. + return len(tokens) + + +def string_to_pydantic_class(class_definition: str): + # Define the namespace where the class will be evaluated + namespace = { + 'BaseModel': BaseModel, + 'typing': typing + } + # Evaluate the class definition in the given namespace + pydantic_class = eval(class_definition, namespace) + return pydantic_class + + +def extract_json(text): + # Find the JSON string in the text + match = re.search(r'\{.*?\}', text, re.DOTALL) + if match: + json_str = match.group() + try: + # Try to load the JSON string + json_obj = json.loads(json_str) + return json_obj + except json.JSONDecodeError: + print("Invalid JSON") + return None + else: + print("No JSON found") + return None diff --git a/poetry.lock b/poetry.lock index c2dfcf6..1b41e7d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2151,4 +2151,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "462340b43d9b25819bc82a2794d9e748ca10940da005c19eafb20df4dfcff316" +content-hash = "3d5f48cd8dffdf723ebcf610411d67067fa63b82ef656c882bb613e85186d49e" diff --git a/pyproject.toml b/pyproject.toml index 195a39d..42a4668 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "extract_thinker" version = "0.0.1" -description = "ORM style library to extract data from documents with LLMs" +description = "Library to extract data from files and documents agnositicaly using LLMs" authors = ["Júlio Almeida "] readme = "README.md" @@ -17,6 +17,8 @@ pypdfium2 = "^4.29.0" instructor = "^1.2.2" python-dotenv = "^1.0.1" cachetools = "^5.3.3" +pyyaml = "^6.0.1" +tiktoken = "^0.6.0" [tool.poetry.dev-dependencies] flake8 = "^3.9.2" diff --git a/tests/classify.py b/tests/classify.py index bc3aa0e..4551016 100644 --- a/tests/classify.py +++ b/tests/classify.py @@ -1,8 +1,10 @@ +import asyncio import os from dotenv import load_dotenv from extract_thinker.extractor import Extractor +from extract_thinker.process import Process from extract_thinker.document_loader.document_loader_tesseract import DocumentLoaderTesseract -from extract_thinker.models import Classification, ClassificationResponse +from extract_thinker.models import classification, classification_response load_dotenv() cwd = os.getcwd() @@ -14,8 +16,8 @@ def test_classify_feature(): test_file_path = os.path.join(cwd, "test_images", "invoice.png") classifications = [ - Classification(name="Driver License", description="This is a driver license"), - Classification(name="Invoice", description="This is an invoice"), + classification(name="Driver License", description="This is a driver license"), + classification(name="Invoice", description="This is an invoice"), ] extractor = Extractor() @@ -27,5 +29,39 @@ def test_classify_feature(): # Assert assert result is not None - assert isinstance(result, ClassificationResponse) + assert isinstance(result, classification_response) + assert result.name == "Invoice" + + +def test_classify(): + # Arrange + test_file_path = os.path.join(cwd, "test_images", "invoice.png") + + process = Process() + tesseract_path = os.getenv("TESSERACT_PATH") + + document_loader = DocumentLoaderTesseract(tesseract_path) + + open35extractor = Extractor(document_loader) + open35extractor.load_llm("gpt-3.5-turbo") + + mistral2extractor = Extractor(document_loader) + mistral2extractor.load_llm("claude-3-haiku-20240307") + + gpt4extractor = Extractor(document_loader) + gpt4extractor.load_llm("gpt-4-turbo") + + process.add_classifyExtractor([[open35extractor, mistral2extractor], [gpt4extractor]]) + + classifications = [ + classification(name="Driver License", description="This is a driver license"), + classification(name="Invoice", description="This is an invoice"), + ] + + # Act + result = asyncio.run(process.classify_async(test_file_path, classifications)) + + # Assert + assert result is not None + assert isinstance(result, classification_response) assert result.name == "Invoice" diff --git a/tests/models/driver_license.py b/tests/models/driver_license.py new file mode 100644 index 0000000..7138dbe --- /dev/null +++ b/tests/models/driver_license.py @@ -0,0 +1,7 @@ +from extract_thinker.models.contract import Contract + + +class DriverLicense(Contract): + name: str + age: int + license_number: str diff --git a/tests/models/invoice.py b/tests/models/invoice.py index 36a3314..5eed8d0 100644 --- a/tests/models/invoice.py +++ b/tests/models/invoice.py @@ -1,4 +1,4 @@ -from extract_thinker.models import Contract +from extract_thinker.models.contract import Contract class InvoiceContract(Contract): From 140e7a3b4253668c65cac2135c68c9d1a35c1066 Mon Sep 17 00:00:00 2001 From: Julio Date: Tue, 14 May 2024 17:15:10 +0100 Subject: [PATCH 4/7] Update README.md --- README.md | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index bc2ccd2..5957991 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,14 @@ +

+ Extract Thinker Logo +

+

+ + Medium + +GitHub Last Commit +Github License +

+ # ExtractThinker Library to extract data from files and documents agnostically using LLMs. `extract_thinker` provides ORM-style interaction between files and LLMs, allowing for flexible and powerful document extraction workflows. @@ -10,6 +21,10 @@ Library to extract data from files and documents agnostically using LLMs. `extra - Built-in support for various document formats. - ORM-style interaction between files and LLMs. +

+ Extract Thinker Features Diagram +

+ ## Installation To install `extract_thinker`, you can use `pip`: @@ -33,7 +48,6 @@ class InvoiceContract(Contract): invoice_number: str invoice_date: str -# Arrange tesseract_path = os.getenv("TESSERACT_PATH") test_file_path = os.path.join(cwd, "test_images", "invoice.png") @@ -43,13 +57,10 @@ extractor.load_document_loader( ) extractor.load_llm("claude-3-haiku-20240307") -# Act result = extractor.extract(test_file_path, InvoiceContract) -# Assert -assert result is not None -assert result.invoice_number == "0000001" -assert result.invoice_date == "2014-05-07" +print("Invoice Number: ", result.invoice_number) +print("Invoice Date: ", result.invoice_date) ``` ## Splitting Files Example @@ -83,8 +94,7 @@ process = Process() process.load_document_loader(DocumentLoaderTesseract(os.getenv("TESSERACT_PATH"))) process.load_splitter(ImageSplitter()) -path = "C:\\Users\\Lopez\\Desktop\\MagniFinance\\examples\\outputTestOne.pdf" -other_path = "C:\\Users\\Lopez\\Desktop\\MagniFinance\\examples\\SingleInvoiceTests\\FT63O.pdf" +path = "..." split_content = process.load_file(path)\ .split(classifications)\ @@ -93,6 +103,17 @@ split_content = process.load_file(path)\ # Process the split_content as needed ``` +## Infrastructure + +The `extract_thinker` project is inspired by the LangChain ecosystem, featuring a modular infrastructure with templates, components, and core functions to facilitate robust document extraction and processing. + +

+ Extract Thinker Logo +

+ +## Why Just Not LangChain? +While LangChain is a generalized framework designed for a wide array of use cases, extract_thinker is specifically focused on Intelligent Document Processing (IDP). Although achieving 100% accuracy in IDP remains a challenge, leveraging LLMs brings us significantly closer to this goal. + ## Additional Examples You can find more examples in the repository. These examples cover various use cases and demonstrate the flexibility of extract_thinker. @@ -104,8 +125,9 @@ Create a new branch for your feature or bugfix. Write tests for your changes. Run tests to ensure everything is working correctly. Submit a pull request with a description of your changes. -License + +## License This project is licensed under the Apache License 2.0. See the LICENSE file for more details. ## Contact -For any questions or issues, please open an issue on the GitHub repository. \ No newline at end of file +For any questions or issues, please open an issue on the GitHub repository. From 909189b31d7e4e59d167fcf304a779e488f5be63 Mon Sep 17 00:00:00 2001 From: Julio Date: Tue, 14 May 2024 17:46:41 +0100 Subject: [PATCH 5/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5957991..9a1a277 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ The `extract_thinker` project is inspired by the LangChain ecosystem, featuring While LangChain is a generalized framework designed for a wide array of use cases, extract_thinker is specifically focused on Intelligent Document Processing (IDP). Although achieving 100% accuracy in IDP remains a challenge, leveraging LLMs brings us significantly closer to this goal. ## Additional Examples -You can find more examples in the repository. These examples cover various use cases and demonstrate the flexibility of extract_thinker. +You can find more examples in the repository. These examples cover various use cases and demonstrate the flexibility of extract_thinker. Also check my the medium of the author that contains several examples about the library ## Contributing We welcome contributions from the community! If you would like to contribute, please follow these steps: From d1fe49e6617d491c146ad8b380dfd7089936d993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Almeida?= Date: Tue, 14 May 2024 17:47:48 +0100 Subject: [PATCH 6/7] launch.json removed from the repo --- .vscode/launch.json | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 6b76b4f..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Python Debugger: Current File", - "type": "debugpy", - "request": "launch", - "program": "${file}", - "console": "integratedTerminal" - } - ] -} \ No newline at end of file From 741b9aa790ee17e3a6500172c5ce85540eedd944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Almeida?= Date: Tue, 14 May 2024 17:51:10 +0100 Subject: [PATCH 7/7] gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9d05283..f143ae3 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,6 @@ Scripts/unstructured-ingest-script.py Scripts/unstructured-ingest.exe Scripts/uvicorn.exe Scripts/vba_extract.py + +# VSCode settings +.vscode/ \ No newline at end of file