From cec8c385111bea9fadde282aec9a6b3018184b4c Mon Sep 17 00:00:00 2001 From: Benjoyo Date: Tue, 26 Mar 2024 14:05:19 +0100 Subject: [PATCH] add amazon and azure AI services and perform refactorings --- .../llm/anthropic_chat/anthropic_chat.py | 2 +- bpm-ai-core/bpm_ai_core/llm/common/blob.py | 175 +++ bpm-ai-core/bpm_ai_core/llm/common/message.py | 6 +- bpm-ai-core/bpm_ai_core/llm/common/tool.py | 2 +- .../bpm_ai_core/ocr/amazon_textract.py | 138 +++ .../bpm_ai_core/ocr/azure_doc_intelligence.py | 78 ++ bpm-ai-core/bpm_ai_core/ocr/ocr.py | 51 +- bpm-ai-core/bpm_ai_core/ocr/tesseract.py | 46 +- bpm-ai-core/bpm_ai_core/prompt/filters.py | 2 +- bpm-ai-core/bpm_ai_core/prompt/prompt.py | 24 +- .../amazon_textract_docvqa.py | 66 + .../azure_doc_intelligence_docvqa.py | 82 ++ .../question_answering/pix2struct_vqa.py | 22 +- .../question_answering/question_answering.py | 27 +- .../question_answering/transformers_docvqa.py | 22 +- .../question_answering/transformers_qa.py | 13 +- .../bpm_ai_core/speech_recognition/asr.py | 12 +- .../speech_recognition/faster_whisper.py | 4 +- .../speech_recognition/openai_whisper.py | 4 +- bpm-ai-core/bpm_ai_core/tracing/decorators.py | 22 +- .../translation/amazon_translate.py | 38 + .../translation/azure_translation.py | 42 + .../translation/easy_nmt/easy_nmt.py | 6 +- bpm-ai-core/bpm_ai_core/translation/nmt.py | 14 +- bpm-ai-core/bpm_ai_core/util/file.py | 2 +- bpm-ai-core/bpm_ai_core/util/image.py | 109 +- .../util/{json.py => json_schema.py} | 0 bpm-ai-core/bpm_ai_core/util/linguistics.py | 186 +++ bpm-ai-core/bpm_ai_core/util/storage.py | 78 ++ bpm-ai-core/poetry.lock | 1073 +++++++++++++---- bpm-ai-core/pyproject.toml | 8 +- bpm-ai-core/tests/invoice-sample.pdf | Bin 0 -> 43627 bytes bpm-ai-core/tests/test.prompt | 4 +- bpm-ai-core/tests/test_blob.py | 19 + bpm-ai-core/tests/test_docvqa.py | 48 +- bpm-ai-core/tests/test_image.py | 32 + bpm-ai-core/tests/test_json.py | 2 +- bpm-ai-core/tests/test_nmt.py | 12 +- bpm-ai-core/tests/test_ocr.py | 91 -- bpm-ai-core/tests/test_prompt.py | 11 +- bpm-ai-core/tests/test_qa.py | 18 +- bpm-ai-core/tests/test_speech.py | 1 - 42 files changed, 2093 insertions(+), 499 deletions(-) create mode 100644 bpm-ai-core/bpm_ai_core/llm/common/blob.py create mode 100644 bpm-ai-core/bpm_ai_core/ocr/amazon_textract.py create mode 100644 bpm-ai-core/bpm_ai_core/ocr/azure_doc_intelligence.py create mode 100644 bpm-ai-core/bpm_ai_core/question_answering/amazon_textract_docvqa.py create mode 100644 bpm-ai-core/bpm_ai_core/question_answering/azure_doc_intelligence_docvqa.py create mode 100644 bpm-ai-core/bpm_ai_core/translation/amazon_translate.py create mode 100644 bpm-ai-core/bpm_ai_core/translation/azure_translation.py rename bpm-ai-core/bpm_ai_core/util/{json.py => json_schema.py} (100%) create mode 100644 bpm-ai-core/bpm_ai_core/util/linguistics.py create mode 100644 bpm-ai-core/bpm_ai_core/util/storage.py create mode 100644 bpm-ai-core/tests/invoice-sample.pdf create mode 100644 bpm-ai-core/tests/test_blob.py create mode 100644 bpm-ai-core/tests/test_image.py delete mode 100644 bpm-ai-core/tests/test_ocr.py diff --git a/bpm-ai-core/bpm_ai_core/llm/anthropic_chat/anthropic_chat.py b/bpm-ai-core/bpm_ai_core/llm/anthropic_chat/anthropic_chat.py index fb92e34..f4869c0 100644 --- a/bpm-ai-core/bpm_ai_core/llm/anthropic_chat/anthropic_chat.py +++ b/bpm-ai-core/bpm_ai_core/llm/anthropic_chat/anthropic_chat.py @@ -13,7 +13,7 @@ from bpm_ai_core.llm.common.tool import Tool from bpm_ai_core.prompt.prompt import Prompt from bpm_ai_core.tracing.tracing import Tracing -from bpm_ai_core.util.json import expand_simplified_json_schema +from bpm_ai_core.util.json_schema import expand_simplified_json_schema logger = logging.getLogger(__name__) diff --git a/bpm-ai-core/bpm_ai_core/llm/common/blob.py b/bpm-ai-core/bpm_ai_core/llm/common/blob.py new file mode 100644 index 0000000..5c27370 --- /dev/null +++ b/bpm-ai-core/bpm_ai_core/llm/common/blob.py @@ -0,0 +1,175 @@ +import contextlib +import mimetypes +import os +from io import BytesIO, BufferedReader +from pathlib import PurePath +from typing import Union, Optional, Dict, Any, Self, cast, Generator + +import requests +from pydantic import BaseModel, Field, model_validator + +from bpm_ai_core.util.storage import read_file_from_azure_blob, read_file_from_s3, is_s3_url, is_azure_blob_url + + +class Blob(BaseModel): + """Blob represents raw data by either reference or value. + + Provides an interface to materialize the blob in different representations. + + Based on: + https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/document_loaders/blob_loaders.py + """ + + data: Union[bytes, str, None] + """Raw data associated with the blob.""" + + mimetype: Optional[str] = None + """MimeType not to be confused with a file extension.""" + + path: Optional[Union[str, PurePath]] = None + """Location where the original content was found.""" + + metadata: Dict[str, Any] = Field(default_factory=dict) + """Metadata about the blob (e.g., source)""" + + class Config: + arbitrary_types_allowed = True + frozen = True + + @property + def source(self) -> Optional[str]: + """The source location of the blob as string if known otherwise none. + + If a path is associated with the blob, it will default to the path location. + + Unless explicitly set via a metadata field called "source", in which + case that value will be used instead. + """ + if self.metadata and "source" in self.metadata: + return cast(Optional[str], self.metadata["source"]) + return str(self.path) if self.path else None + + @model_validator(mode='after') + def check_blob_is_valid(self) -> Self: + """Verify that either data or path is provided.""" + if not self.data and not self.path: + raise ValueError("Either data or path must be provided") + return self + + def is_image(self) -> bool: + return self.mimetype.startswith("image/") if self.mimetype else False + + def is_pdf(self) -> bool: + return self.mimetype == "application/pdf" if self.mimetype else False + + def is_audio(self) -> bool: + return self.mimetype.startswith("audio/") if self.mimetype else False + + def is_video(self) -> bool: + return self.mimetype.startswith("video/") if self.mimetype else False + + def is_text(self) -> bool: + app_text_mimetypes = [ + 'application/json', + 'application/javascript', + 'application/manifest+json', + 'application/xml', + 'application/x-sh', + 'application/x-python', + ] + return (self.mimetype.startswith("text/") or self.mimetype in app_text_mimetypes) if self.mimetype else False + + async def as_bytes(self) -> bytes: + """Read data as bytes.""" + if self.data is None and (self.path.startswith('http://') or self.path.startswith('https://')): + response = requests.get(self.path) + return response.content + elif self.data is None and is_s3_url(self.path): + return await read_file_from_s3(self.path) + elif self.data is None and is_azure_blob_url(self.path): + return await read_file_from_azure_blob(self.path) + elif isinstance(self.data, bytes): + return self.data + elif isinstance(self.data, str): + return self.data.encode("utf-8") + elif self.data is None and self.path: + with open(str(self.path), "rb") as f: + return f.read() + else: + raise ValueError(f"Unable to get bytes for blob {self}") + + async def as_bytes_io(self) -> BytesIO: + return BytesIO(await self.as_bytes()) + + @classmethod + def from_path_or_url( + cls, + path: Union[str, PurePath], + *, + mime_type: Optional[str] = None, + guess_type: bool = True, + metadata: Optional[dict] = None, + ) -> "Blob": + """Load the blob from a path like object. + + Args: + path: path like object to file to be read + mime_type: if provided, will be set as the mime-type of the data + guess_type: If True, the mimetype will be guessed from the file extension, + if a mime-type was not provided + metadata: Metadata to associate with the blob + + Returns: + Blob instance + """ + if mime_type is None and guess_type: + _mimetype = mimetypes.guess_type(path)[0] if guess_type else None + else: + _mimetype = mime_type + + # Convert a path to an absolute path + if os.path.isfile(path): + path = os.path.abspath(path) + + # We do not load the data immediately, instead we treat the blob as a + # reference to the underlying data. + return cls( + data=None, + mimetype=_mimetype, + path=path, + metadata=metadata if metadata is not None else {}, + ) + + @classmethod + def from_data( + cls, + data: Union[str, bytes], + *, + mime_type: str, + path: Optional[str] = None, + metadata: Optional[dict] = None, + ) -> "Blob": + """Initialize the blob from in-memory data. + + Args: + data: the in-memory data associated with the blob + mime_type: if provided, will be set as the mime-type of the data + path: if provided, will be set as the source from which the data came + metadata: Metadata to associate with the blob + + Returns: + Blob instance + """ + return cls( + data=data, + mimetype=mime_type, + path=path, + metadata=metadata if metadata is not None else {}, + ) + + def __repr__(self) -> str: + """Define the blob representation.""" + str_repr = f"Blob {id(self)}" + if self.source: + str_repr += f" {self.source}" + return str_repr \ No newline at end of file diff --git a/bpm-ai-core/bpm_ai_core/llm/common/message.py b/bpm-ai-core/bpm_ai_core/llm/common/message.py index 3468398..1f85a9d 100644 --- a/bpm-ai-core/bpm_ai_core/llm/common/message.py +++ b/bpm-ai-core/bpm_ai_core/llm/common/message.py @@ -3,19 +3,19 @@ import json from typing import Optional, Literal, Any, Union, List -from PIL import Image from pydantic import BaseModel, Field, ConfigDict +from bpm_ai_core.llm.common.blob import Blob from bpm_ai_core.llm.common.tool import Tool from bpm_ai_core.tracing.tracing import Tracing class ChatMessage(BaseModel): - content: Optional[Union[str, dict, List[Union[str, Image.Image]]]] = None + content: Optional[Union[str, dict, List[Union[str, Blob]]]] = None """ The contents of the message. Either a string for normal completions, - or a list of strings and images for multimodal completions, + or a list of strings and blobs for multimodal completions, or a dict for prediction with output schema. """ diff --git a/bpm-ai-core/bpm_ai_core/llm/common/tool.py b/bpm-ai-core/bpm_ai_core/llm/common/tool.py index 721626c..ea2cb01 100644 --- a/bpm-ai-core/bpm_ai_core/llm/common/tool.py +++ b/bpm-ai-core/bpm_ai_core/llm/common/tool.py @@ -3,7 +3,7 @@ from pydantic import BaseModel, validate_call, create_model -from bpm_ai_core.util.json import expand_simplified_json_schema +from bpm_ai_core.util.json_schema import expand_simplified_json_schema def _create_subset_model( diff --git a/bpm-ai-core/bpm_ai_core/ocr/amazon_textract.py b/bpm-ai-core/bpm_ai_core/ocr/amazon_textract.py new file mode 100644 index 0000000..c7a166c --- /dev/null +++ b/bpm-ai-core/bpm_ai_core/ocr/amazon_textract.py @@ -0,0 +1,138 @@ +import asyncio + +from typing_extensions import override + +from bpm_ai_core.llm.common.blob import Blob +from bpm_ai_core.ocr.ocr import OCR, OCRResult, OCRPage +from bpm_ai_core.util.image import blob_as_images +from bpm_ai_core.util.storage import is_s3_url, parse_s3_url + +try: + from aiobotocore.session import get_session + from textractprettyprinter.t_pretty_print import get_text_from_layout_json + + has_textract = True +except ImportError: + has_textract = False + +IMAGE_FORMATS = ["png", "jpeg", "tiff"] + + +class AmazonTextractOCR(OCR): + def __init__(self, region_name: str = None): + if not has_textract: + raise ImportError('aiobotocore and/or amazon-textract-prettyprinter are not installed') + self.region_name = region_name + + @override + async def _do_process( + self, + blob: Blob, + language: str = None + ) -> OCRResult: + if not (blob.is_pdf() or blob.is_image()): + raise ValueError("Blob must be a PDF or an image") + + if is_s3_url(blob.path): + pages = await self._get_pages_async(blob.path) + else: + pages = await self._get_pages_sync(blob) + + return OCRResult(pages=pages) + + async def _get_pages_sync(self, document: Blob): + if document.is_pdf(): + _bytes = await document.as_bytes() + else: + _bytes = (await blob_as_images(document, accept_formats=IMAGE_FORMATS, return_bytes=True))[0] + # Create a document from the image bytes asynchronously + async with get_session().create_client("textract", region_name=self.region_name) as client: + # Call Amazon Textract API asynchronously + response = await client.analyze_document( + Document={"Bytes": _bytes}, + FeatureTypes=["TABLES", "FORMS", "LAYOUT"] + ) + # Convert Textract response to markdown using amazon-textract-prettyprinter + markdown_pages = get_text_from_layout_json( + textract_json=response, + table_format="github", + generate_markdown=True + ) + return self.parse_pages(markdown_pages, response) + + async def _get_pages_async(self, s3_url: str): + bucket_name, file_path = await parse_s3_url(s3_url) + # Create a document from the image bytes asynchronously + async with get_session().create_client("textract", region_name=self.region_name) as client: + # Call Amazon Textract API asynchronously using start_document_analysis + response = await client.start_document_analysis( + DocumentLocation={'S3Object': { + 'Bucket': bucket_name, + 'Name': file_path + }}, + FeatureTypes=["TABLES", "FORMS", "LAYOUT"] + ) + + # Get the job ID from the response + job_id = response["JobId"] + + # Wait for the job to complete + while True: + response = await client.get_document_analysis(JobId=job_id) + status = response["JobStatus"] + if status in ["SUCCEEDED", "FAILED"]: + break + await asyncio.sleep(1) # Wait for 1 second before checking the status again + + if status == "FAILED": + raise Exception(f"Document analysis failed with error: {response['StatusMessage']}") + + # Retrieve the results from the completed job + pages = [] + markdown_pages = {} + next_token = None + while True: + if next_token: + response = await client.get_document_analysis(JobId=job_id, NextToken=next_token) + else: + response = await client.get_document_analysis(JobId=job_id) + + # Convert Textract response to markdown using amazon-textract-prettyprinter + markdown_pages.update(get_text_from_layout_json( + textract_json=response, + table_format="github", + generate_markdown=True + )) + pages.extend(self.parse_pages(markdown_pages, response)) + + next_token = response.get("NextToken") + if not next_token: + break + return pages + + @staticmethod + def parse_pages(markdown_pages, response): + pages = [] + page_idx = -1 + for block in response["Blocks"]: + if block["BlockType"] == "PAGE": + page_idx += 1 + bboxes = [] + words = [] + for _block in block["Relationships"][0]["Ids"]: + block_data = next(b for b in response["Blocks"] if b["Id"] == _block) + if block_data["BlockType"] == "LINE": + for word_block_id in block_data["Relationships"][0]["Ids"]: + word_block = next(b for b in response["Blocks"] if b["Id"] == word_block_id) + if word_block["BlockType"] == "WORD": + bbox = word_block["Geometry"]["BoundingBox"] + x, y, w, h = bbox["Left"], bbox["Top"], bbox["Width"], bbox["Height"] + bboxes.append((x, y, x + w, y + h)) + words.append(word_block["Text"]) + page_data = OCRPage( + text=list(markdown_pages.values())[page_idx], + words=words, + bboxes=bboxes + ) + pages.append(page_data) + return pages diff --git a/bpm-ai-core/bpm_ai_core/ocr/azure_doc_intelligence.py b/bpm-ai-core/bpm_ai_core/ocr/azure_doc_intelligence.py new file mode 100644 index 0000000..84bdff5 --- /dev/null +++ b/bpm-ai-core/bpm_ai_core/ocr/azure_doc_intelligence.py @@ -0,0 +1,78 @@ +import logging +import os +from io import BytesIO +from typing_extensions import override + +from bpm_ai_core.llm.common.blob import Blob +from bpm_ai_core.ocr.ocr import OCR, OCRResult, OCRPage +from bpm_ai_core.util.image import blob_as_images + +try: + from azure.ai.documentintelligence.aio import DocumentIntelligenceClient as AsyncDocumentIntelligenceClient + from azure.core.credentials import AzureKeyCredential + + has_azure_doc = True +except ImportError: + has_azure_doc = False + +azure_logger = logging.getLogger('azure') +azure_logger.setLevel(logging.WARNING) + +IMAGE_FORMATS = ["png", "jpeg", "tiff"] + + +class AzureOCR(OCR): + def __init__(self, endpoint: str = None): + if not has_azure_doc: + raise ImportError('azure-ai-documentintelligence is not installed') + self.endpoint = endpoint or os.environ.get("AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT") + + @override + async def _do_process( + self, + blob: Blob, + language: str = None + ) -> OCRResult: + if not (blob.is_pdf() or blob.is_image()): + raise ValueError("Blob must be a PDF or an image") + if blob.is_pdf(): + bytes_io = await blob.as_bytes_io() + else: + bytes_io = BytesIO((await blob_as_images(blob, accept_formats=IMAGE_FORMATS, return_bytes=True))[0]) + + async with AsyncDocumentIntelligenceClient( + self.endpoint, + AzureKeyCredential( + os.environ.get("AZURE_DOCUMENT_INTELLIGENCE_KEY") + ) + ) as client: + document = await client.begin_analyze_document( + model_id="prebuilt-layout", + analyze_request=bytes_io, + content_type="application/octet-stream", + output_content_format="markdown" + ) + + # Wait for the extraction to complete asynchronously + result = await document.result() + markdown_content = result.content + + pages = [] + for page in result.pages: + bboxes = [] + words = [] + for word in page.words: + polygon = word.polygon + x, y = polygon[0], polygon[1] + w, h = polygon[2] - x, polygon[5] - y + bboxes.append((x / page['width'], y / page['height'], (x + w) / page['width'], (y + h) / page['height'])) + words.append(word.content) + + page_data = OCRPage( + text=" ".join(words), + words=words, + bboxes=bboxes + ) + pages.append(page_data) + + return OCRResult(pages=pages) diff --git a/bpm-ai-core/bpm_ai_core/ocr/ocr.py b/bpm-ai-core/bpm_ai_core/ocr/ocr.py index 75b2e70..2d54a65 100644 --- a/bpm-ai-core/bpm_ai_core/ocr/ocr.py +++ b/bpm-ai-core/bpm_ai_core/ocr/ocr.py @@ -1,14 +1,24 @@ from abc import ABC, abstractmethod +from typing import Tuple +from pydantic import BaseModel, Field -from PIL.Image import Image -from pydantic import BaseModel +from bpm_ai_core.llm.common.blob import Blob +from bpm_ai_core.tracing.decorators import span -from bpm_ai_core.tracing.tracing import Tracing -from bpm_ai_core.util.image import load_images + +class OCRPage(BaseModel): + text: str + words: list[str] = Field(..., exclude=True) + bboxes: list[Tuple[float, float, float, float]] = Field(..., exclude=True) + """Format: (x, y, x + w, y + h), normalized to 1""" class OCRResult(BaseModel): - texts: list[str] + pages: list[OCRPage] + + @property + def full_text(self) -> str: + return "\n".join([p.text for p in self.pages]) class OCR(ABC): @@ -17,31 +27,24 @@ class OCR(ABC): """ @abstractmethod - async def images_to_text_with_metadata( + async def _do_process( self, - images: list[Image], + blob: Blob, language: str = None ) -> OCRResult: pass - async def images_to_text( + @span(name="ocr") + async def process( self, - image_or_path: Image | list[Image] | str, + blob_or_path: Blob | str, language: str = None - ) -> str: - Tracing.tracers().start_span("ocr", inputs={ - "image": image_or_path, - "language": language - }) - if isinstance(image_or_path, str): - images = load_images(image_or_path) - elif isinstance(image_or_path, Image): - images = [image_or_path] - else: # list[Image] - images = image_or_path - result = await self.images_to_text_with_metadata( - images=images, + ) -> OCRResult: + if isinstance(blob_or_path, str): + blob = Blob.from_path_or_url(blob_or_path) + else: # Blob + blob = blob_or_path + return await self._do_process( + blob=blob, language=language ) - Tracing.tracers().end_span(outputs={"result": result.model_dump()}) - return "\n".join(result.texts) diff --git a/bpm-ai-core/bpm_ai_core/ocr/tesseract.py b/bpm-ai-core/bpm_ai_core/ocr/tesseract.py index 015b732..72935a4 100644 --- a/bpm-ai-core/bpm_ai_core/ocr/tesseract.py +++ b/bpm-ai-core/bpm_ai_core/ocr/tesseract.py @@ -1,10 +1,14 @@ import logging import os import urllib +from io import BytesIO +from typing_extensions import override -from PIL.Image import Image +from PIL import Image -from bpm_ai_core.ocr.ocr import OCR, OCRResult +from bpm_ai_core.llm.common.blob import Blob +from bpm_ai_core.ocr.ocr import OCR, OCRResult, OCRPage +from bpm_ai_core.util.image import pdf_to_images from bpm_ai_core.util.language import indentify_language_iso_639_3 try: @@ -30,17 +34,47 @@ def __init__(self): os.makedirs(TESSDATA_DIR, exist_ok=True) os.environ["TESSDATA_PREFIX"] = TESSDATA_DIR - async def images_to_text_with_metadata( + @override + async def _do_process( self, - images: list[Image], + blob: Blob, language: str = None ) -> OCRResult: + if blob.is_pdf(): + images = pdf_to_images(await blob.as_bytes()) + elif blob.is_image(): + images = [Image.open(await blob.as_bytes_io())] + else: + raise ValueError("Blob must be a PDF or an image") if language is None: language = self.identify_image_language(images[0]) logger.info(f"tesseract: auto detected language '{language}'") self.download_if_missing(language) - texts = [pytesseract.image_to_string(image, lang=language) for image in images] - return OCRResult(texts=texts) + + pages = [] + for image in images: + text = pytesseract.image_to_string(image) + data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT) + bboxes = [] + words = [] + + # For each word in the data... + for i in range(len(data['text'])): + # ...if the word isn't empty + if data['text'][i].strip(): + x, y, w, h = data['left'][i], data['top'][i], data['width'][i], data['height'][i] + # add bounding box + bboxes.append((x / image.width, y / image.height, (x + w) / image.width, (y + h) / image.height)) + words.append(data['text'][i]) + + page = OCRPage( + text=text, + words=words, + bboxes=bboxes + ) + pages.append(page) + + return OCRResult(pages=pages) def identify_image_language(self, image: Image) -> str: self.download_if_missing('eng') diff --git a/bpm-ai-core/bpm_ai_core/prompt/filters.py b/bpm-ai-core/bpm_ai_core/prompt/filters.py index 772c653..aaed6a6 100644 --- a/bpm-ai-core/bpm_ai_core/prompt/filters.py +++ b/bpm-ai-core/bpm_ai_core/prompt/filters.py @@ -1,7 +1,7 @@ import json from bpm_ai_core.util.markdown import dict_to_md -from bpm_ai_core.util.xml import dict_to_xml as _dict_to_xml +from bpm_ai_core.util.xml_convert import dict_to_xml as _dict_to_xml def dict_to_markdown(d: dict) -> str: diff --git a/bpm-ai-core/bpm_ai_core/prompt/prompt.py b/bpm-ai-core/bpm_ai_core/prompt/prompt.py index 35dc58b..00a702d 100644 --- a/bpm-ai-core/bpm_ai_core/prompt/prompt.py +++ b/bpm-ai-core/bpm_ai_core/prompt/prompt.py @@ -5,10 +5,10 @@ from jinja2 import Template, environment +from bpm_ai_core.llm.common.blob import Blob from bpm_ai_core.llm.common.message import ChatMessage, AssistantMessage, ToolResultMessage, ToolCallMessage, \ UserMessage, SystemMessage from bpm_ai_core.prompt.filters import dict_to_markdown, dict_to_xml, dict_to_json -from bpm_ai_core.util.image import load_images environment.DEFAULT_FILTERS['markdown'] = dict_to_markdown environment.DEFAULT_FILTERS['json'] = dict_to_json @@ -43,7 +43,7 @@ def format(self, llm_name: str = "") -> List[ChatMessage]: full_prompt = template.render(self.template_vars) regex = r'\[#\s*(user|assistant|system|tool_result:.*|)\s*#\]' - image_regex = r'\[#\s*image\s*(.*?)\s*#\]' + blob_regex = r'\[#\s*blob\s*(.*?)\s*#\]' tool_call_regex = r'\[#\s*tool_call:\s*(.*?)\s*#\]' # Check if the raw template contains message prompt sections @@ -65,21 +65,23 @@ def format(self, llm_name: str = "") -> List[ChatMessage]: content_parts = [] tool_calls = [] - # todo can't use images and call tools at the same time right now (isn't possible right now anyway) - # Check for images in the content and replace with Image objects - if re.search(image_regex, content): + # todo can't use images and call tools at the same time right now + # Check for blobs (images, videos, ...) in the content and replace with Blob objects + if re.search(blob_regex, content): start = 0 - for match in re.finditer(image_regex, content): - # Add the text before the image + for match in re.finditer(blob_regex, content): + # Add the text before the blob before_text = content[start:match.start()].strip() if before_text: content_parts.append(before_text) - # Add the image - image_url = match.group(1) - content_parts.extend(load_images(image_url)) + # Add the blob + blob_url = match.group(1) + content_parts.append( + Blob.from_path_or_url(blob_url) + ) start = match.end() - # Add the remaining text after the last image + # Add the remaining text after the last blob content_parts.append(content[start:].strip()) # Check for tool calls in the content diff --git a/bpm-ai-core/bpm_ai_core/question_answering/amazon_textract_docvqa.py b/bpm-ai-core/bpm_ai_core/question_answering/amazon_textract_docvqa.py new file mode 100644 index 0000000..6f6aaf8 --- /dev/null +++ b/bpm-ai-core/bpm_ai_core/question_answering/amazon_textract_docvqa.py @@ -0,0 +1,66 @@ +import logging +from typing_extensions import override + +from bpm_ai_core.llm.common.blob import Blob +from bpm_ai_core.question_answering.question_answering import QuestionAnswering, QAResult +from bpm_ai_core.util.image import blob_as_images + +try: + from aiobotocore.session import get_session + + has_textract = True +except ImportError: + has_textract = False + +logger = logging.getLogger(__name__) + +IMAGE_FORMATS = ["png", "jpeg", "tiff"] + + +class AmazonTextractDocVQA(QuestionAnswering): + """ + + """ + def __init__(self, region_name: str = None): + if not has_textract: + raise ImportError('aiobotocore is not installed') + self.region_name = region_name + + @override + async def _do_answer( + self, + context_str_or_blob: str | Blob, + question: str + ) -> QAResult: + if isinstance(context_str_or_blob, str) or not (context_str_or_blob.is_image() or context_str_or_blob.is_pdf()): + raise Exception('AmazonTextractDocVQA only supports image or PDF input') + if context_str_or_blob.is_pdf(): + _bytes = await context_str_or_blob.as_bytes() + else: + _bytes = (await blob_as_images(context_str_or_blob, accept_formats=IMAGE_FORMATS, return_bytes=True))[0] + + async with get_session().create_client("textract", region_name=self.region_name) as client: + response = await client.analyze_document( + Document={"Bytes": _bytes}, + FeatureTypes=["QUERIES"], + QueriesConfig={'Queries': [ + {'Text': question} + ]} + ) + + prediction = next((b for b in response['Blocks'] if b["BlockType"] == "QUERY_RESULT"), {}) + + logger.debug(f"prediction: {prediction}") + + if prediction is None: + raise Exception('AmazonTextractDocVQA failed to extract information.') + + return QAResult( + answer=prediction['Text'], + score=prediction['Confidence'] / 100.0, + start_index=None, + end_index=None, + ) + + + diff --git a/bpm-ai-core/bpm_ai_core/question_answering/azure_doc_intelligence_docvqa.py b/bpm-ai-core/bpm_ai_core/question_answering/azure_doc_intelligence_docvqa.py new file mode 100644 index 0000000..553ca38 --- /dev/null +++ b/bpm-ai-core/bpm_ai_core/question_answering/azure_doc_intelligence_docvqa.py @@ -0,0 +1,82 @@ +import logging +import os +import re +from io import BytesIO +from typing_extensions import override + +from bpm_ai_core.llm.common.blob import Blob +from bpm_ai_core.question_answering.question_answering import QuestionAnswering, QAResult +from bpm_ai_core.util.image import blob_as_images +from bpm_ai_core.util.linguistics import stopwords + +try: + from azure.ai.documentintelligence.aio import DocumentIntelligenceClient as AsyncDocumentIntelligenceClient + from azure.ai.documentintelligence.models import DocumentAnalysisFeature + from azure.core.credentials import AzureKeyCredential + + has_azure_doc = True +except ImportError: + has_azure_doc = False + +azure_logger = logging.getLogger('azure') +azure_logger.setLevel(logging.WARNING) + +logger = logging.getLogger(__name__) + +IMAGE_FORMATS = ["png", "jpeg", "tiff"] + + +class AzureDocVQA(QuestionAnswering): + def __init__(self, endpoint: str = None): + if not has_azure_doc: + raise ImportError('azure-ai-documentintelligence is not installed') + self.endpoint = endpoint or os.environ.get("AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT") + + @override + async def _do_answer( + self, + context_str_or_blob: str | Blob, + question: str + ) -> QAResult: + if isinstance(context_str_or_blob, str) or not (context_str_or_blob.is_image() or context_str_or_blob.is_pdf()): + raise ValueError("Blob must be a PDF or an image") + if context_str_or_blob.is_pdf(): + bytes_io = await context_str_or_blob.as_bytes_io() + else: + bytes_io = BytesIO((await blob_as_images(context_str_or_blob, accept_formats=IMAGE_FORMATS, return_bytes=True))[0]) + + question = self.to_camel_case(question) + logger.info(f"Modified query: '{question}'") + + async with AsyncDocumentIntelligenceClient( + self.endpoint, + AzureKeyCredential( + os.environ.get("AZURE_DOCUMENT_INTELLIGENCE_KEY") + ) + ) as client: + document = await client.begin_analyze_document( + model_id="prebuilt-layout", + analyze_request=bytes_io, + content_type="application/octet-stream", + features=[DocumentAnalysisFeature.QUERY_FIELDS], + query_fields=[question] + ) + + # Wait for the extraction to complete asynchronously + result = await document.result() + prediction = result['documents'][0]['fields'][question] + + return QAResult( + answer=prediction['content'], + score=prediction['confidence'], + start_index=None, + end_index=None, + ) + + @staticmethod + def to_camel_case(text: str) -> str: + text = text.lower() + text = re.sub(r'[^a-zA-Z0-9\s]', '', text) + words = [word for word in text.split() if word not in stopwords] + camel_case_words = [words[0]] + [word.capitalize() for word in words[1:]] + return ''.join(camel_case_words) diff --git a/bpm-ai-core/bpm_ai_core/question_answering/pix2struct_vqa.py b/bpm-ai-core/bpm_ai_core/question_answering/pix2struct_vqa.py index aa6320f..7a4967e 100644 --- a/bpm-ai-core/bpm_ai_core/question_answering/pix2struct_vqa.py +++ b/bpm-ai-core/bpm_ai_core/question_answering/pix2struct_vqa.py @@ -1,8 +1,9 @@ import logging +from typing_extensions import override -from PIL.Image import Image - +from bpm_ai_core.llm.common.blob import Blob from bpm_ai_core.question_answering.question_answering import QuestionAnswering, QAResult +from bpm_ai_core.util.image import blob_as_images try: from transformers import pipeline, AutoTokenizer, DocumentQuestionAnsweringPipeline, \ @@ -15,6 +16,8 @@ logger = logging.getLogger(__name__) +IMAGE_FORMATS = ["png", "jpeg"] + class Pix2StructVQA(QuestionAnswering): """ @@ -31,25 +34,24 @@ def __init__( raise ImportError('transformers is not installed') self.model = model - def answer_with_metadata( + @override + async def _do_answer( self, - context: str | Image, + context_str_or_blob: str | Blob, question: str ) -> QAResult: - if not isinstance(context, Image): - raise Exception('Pix2StructVQA only supports image input') + if isinstance(context_str_or_blob, str) or not (context_str_or_blob.is_image() or context_str_or_blob.is_pdf()): + raise Exception('Pix2StructVQA only supports image or PDF input') + images = await blob_as_images(context_str_or_blob, accept_formats=IMAGE_FORMATS) pix2Struct = Pix2StructForConditionalGeneration.from_pretrained(self.model) pix2Struct.config.vocab_size = 50244 processor = Pix2StructProcessor.from_pretrained(self.model) - inputs = processor(images=context, text=question, return_tensors="pt") + inputs = processor(images=images, text=question, return_tensors="pt") predictions = pix2Struct.generate(**inputs, return_dict_in_generate=True, output_scores=True) prediction = processor.decode(predictions.sequences[0], skip_special_tokens=True) - transition_scores = pix2Struct.compute_transition_scores(predictions.sequences, predictions.scores, normalize_logits=True) - transition_proba = torch.exp(transition_scores)[0] - logger.debug(f"prediction: {prediction}") return QAResult( diff --git a/bpm-ai-core/bpm_ai_core/question_answering/question_answering.py b/bpm-ai-core/bpm_ai_core/question_answering/question_answering.py index 9f7f6c4..e4266b3 100644 --- a/bpm-ai-core/bpm_ai_core/question_answering/question_answering.py +++ b/bpm-ai-core/bpm_ai_core/question_answering/question_answering.py @@ -1,9 +1,9 @@ from abc import ABC, abstractmethod -from PIL.Image import Image from pydantic import BaseModel -from bpm_ai_core.tracing.tracing import Tracing +from bpm_ai_core.llm.common.blob import Blob +from bpm_ai_core.tracing.decorators import span class QAResult(BaseModel): @@ -19,30 +19,25 @@ class QuestionAnswering(ABC): """ @abstractmethod - def answer_with_metadata( + async def _do_answer( self, - context: str | Image, + context_str_or_blob: str | Blob, question: str ) -> QAResult: pass - def answer( + @span(name="qa") + async def answer( self, - context: str | Image, + context_str_or_blob: str | Blob, question: str, confidence_threshold: float | None = 0.1 - ) -> str: - Tracing.tracers().start_span("qa", inputs={ - "context": context, - "question": question, - "confidence_threshold": confidence_threshold - }) - result = self.answer_with_metadata( - context=context, + ) -> QAResult | None: + result = await self._do_answer( + context_str_or_blob=context_str_or_blob, question=question ) - Tracing.tracers().end_span(outputs={"result": result.model_dump()}) # Only return the answer if the score is above the threshold (if given) - return result.answer \ + return result \ if not confidence_threshold or result.score > confidence_threshold \ else None diff --git a/bpm-ai-core/bpm_ai_core/question_answering/transformers_docvqa.py b/bpm-ai-core/bpm_ai_core/question_answering/transformers_docvqa.py index 812b19b..6800a84 100644 --- a/bpm-ai-core/bpm_ai_core/question_answering/transformers_docvqa.py +++ b/bpm-ai-core/bpm_ai_core/question_answering/transformers_docvqa.py @@ -1,8 +1,9 @@ import logging +from typing_extensions import override -from PIL.Image import Image - +from bpm_ai_core.llm.common.blob import Blob from bpm_ai_core.question_answering.question_answering import QuestionAnswering, QAResult +from bpm_ai_core.util.image import blob_as_images try: from transformers import pipeline, AutoTokenizer, DocumentQuestionAnsweringPipeline @@ -13,6 +14,8 @@ logger = logging.getLogger(__name__) +IMAGE_FORMATS = ["png", "jpeg"] + class TransformersDocVQA(QuestionAnswering): """ @@ -26,19 +29,24 @@ def __init__(self, model: str = "naver-clova-ix/donut-base-finetuned-docvqa"): raise ImportError('transformers is not installed') self.model = model - def answer_with_metadata( + @override + async def _do_answer( self, - context: str | Image, + context_str_or_blob: str | Blob, question: str ) -> QAResult: - if not isinstance(context, Image): - raise Exception('TransformersExtractiveDocVQA only supports image input') + if isinstance(context_str_or_blob, str) or not (context_str_or_blob.is_image() or context_str_or_blob.is_pdf()): + raise Exception('TransformersExtractiveDocVQA only supports image or PDF input') + images = await blob_as_images(context_str_or_blob, accept_formats=IMAGE_FORMATS) + + if len(images) > 1: + logger.warning('Multiple images provided, using only first image.') qa_model = pipeline("document-question-answering", model=self.model) prediction = qa_model( question=question, - image=context + image=images[0] )[0] logger.debug(f"prediction: {prediction}") diff --git a/bpm-ai-core/bpm_ai_core/question_answering/transformers_qa.py b/bpm-ai-core/bpm_ai_core/question_answering/transformers_qa.py index b61a90d..63e3a0d 100644 --- a/bpm-ai-core/bpm_ai_core/question_answering/transformers_qa.py +++ b/bpm-ai-core/bpm_ai_core/question_answering/transformers_qa.py @@ -1,7 +1,7 @@ import logging +from typing_extensions import override -from PIL.Image import Image - +from bpm_ai_core.llm.common.blob import Blob from bpm_ai_core.question_answering.question_answering import QuestionAnswering, QAResult try: @@ -25,13 +25,16 @@ def __init__(self, model: str = "deepset/deberta-v3-large-squad2"): raise ImportError('transformers is not installed') self.model = model - def answer_with_metadata( + @override + async def _do_answer( self, - context: str | Image, + context_str_or_blob: str | Blob, question: str ) -> QAResult: - if not isinstance(context, str): + if not isinstance(context_str_or_blob, str): raise Exception('TransformersExtractiveQA only supports string input') + else: + context = context_str_or_blob qa_model = pipeline("question-answering", model=self.model) diff --git a/bpm-ai-core/bpm_ai_core/speech_recognition/asr.py b/bpm-ai-core/bpm_ai_core/speech_recognition/asr.py index 252155b..5f82675 100644 --- a/bpm-ai-core/bpm_ai_core/speech_recognition/asr.py +++ b/bpm-ai-core/bpm_ai_core/speech_recognition/asr.py @@ -2,6 +2,7 @@ from abc import ABC, abstractmethod from typing import Optional, Union +from bpm_ai_core.tracing.decorators import span from bpm_ai_core.tracing.tracing import Tracing from bpm_ai_core.util.audio import load_audio @@ -12,18 +13,13 @@ class ASRModel(ABC): """ @abstractmethod - async def _transcribe(self, audio: Union[str, io.BytesIO], language: Optional[str] = None) -> str: + async def _do_transcribe(self, audio: io.BytesIO, language: Optional[str] = None) -> str: pass + @span(name="asr") async def transcribe(self, audio_or_path: Union[io.BytesIO, str], language: Optional[str] = None) -> str: - Tracing.tracers().start_span("asr", inputs={ - "language": language, - "audio": audio_or_path if isinstance(audio_or_path, str) else "" - }) if isinstance(audio_or_path, str): audio = load_audio(audio_or_path) else: audio = audio_or_path - transcription = await self._transcribe(audio, language) - Tracing.tracers().end_span(outputs={"transcription": transcription}) - return transcription + return await self._do_transcribe(audio, language) diff --git a/bpm-ai-core/bpm_ai_core/speech_recognition/faster_whisper.py b/bpm-ai-core/bpm_ai_core/speech_recognition/faster_whisper.py index 39ec9d8..720af37 100644 --- a/bpm-ai-core/bpm_ai_core/speech_recognition/faster_whisper.py +++ b/bpm-ai-core/bpm_ai_core/speech_recognition/faster_whisper.py @@ -1,4 +1,5 @@ import io +from typing_extensions import override from bpm_ai_core.speech_recognition.asr import ASRModel @@ -31,6 +32,7 @@ def __init__( compute_type=compute_type ) - async def _transcribe(self, audio: io.BytesIO, language: str = None) -> str: + @override + async def _do_transcribe(self, audio: io.BytesIO, language: str = None) -> str: segments, info = self.model.transcribe(audio, language=language) return "".join([s.text for s in list(segments)]) diff --git a/bpm-ai-core/bpm_ai_core/speech_recognition/openai_whisper.py b/bpm-ai-core/bpm_ai_core/speech_recognition/openai_whisper.py index 4617182..c632aa0 100644 --- a/bpm-ai-core/bpm_ai_core/speech_recognition/openai_whisper.py +++ b/bpm-ai-core/bpm_ai_core/speech_recognition/openai_whisper.py @@ -1,6 +1,7 @@ import io import logging from typing import Optional +from typing_extensions import override from bpm_ai_core.speech_recognition.asr import ASRModel @@ -39,7 +40,8 @@ def __init__( raise ImportError('openai is not installed') self.whisper_model = whisper_model - async def _transcribe(self, audio: io.BytesIO, language: Optional[str] = None) -> str: + @override + async def _do_transcribe(self, audio: io.BytesIO, language: Optional[str] = None) -> str: transcript = await client.audio.transcriptions.create( model=self.whisper_model, file=audio, diff --git a/bpm-ai-core/bpm_ai_core/tracing/decorators.py b/bpm-ai-core/bpm_ai_core/tracing/decorators.py index 5de70ba..951ff21 100644 --- a/bpm-ai-core/bpm_ai_core/tracing/decorators.py +++ b/bpm-ai-core/bpm_ai_core/tracing/decorators.py @@ -1,6 +1,8 @@ import inspect from functools import wraps +from pydantic import BaseModel + from bpm_ai_core.tracing.tracing import Tracing @@ -10,12 +12,17 @@ def decorator(func): async def wrapper(*args, **kwargs): trace_name = func.__name__ if name is None else name params = inspect.signature(func).parameters - inputs = {**{list(params.keys())[i]: arg for i, arg in enumerate(args)}, **kwargs} + inputs = {**{list(params.keys())[i]: arg for i, arg in enumerate(args) if list(params.keys())[i] != 'self'}, **kwargs} Tracing.tracers().start_trace(trace_name, inputs, tags or []) try: - result = await func(*args, **kwargs) + if inspect.iscoroutinefunction(func): + result = await func(*args, **kwargs) + else: + result = func(*args, **kwargs) if isinstance(result, dict): outputs = result + elif isinstance(result, BaseModel): + outputs = result.model_dump() else: outputs = {'output': result} Tracing.tracers().end_trace(outputs) @@ -30,15 +37,20 @@ async def wrapper(*args, **kwargs): def span(name: str = None): def decorator(func): @wraps(func) - def wrapper(*args, **kwargs): + async def wrapper(*args, **kwargs): span_name = func.__name__ if name is None else name params = inspect.signature(func).parameters - inputs = {**{list(params.keys())[i]: arg for i, arg in enumerate(args)}, **kwargs} + inputs = {**{list(params.keys())[i]: arg for i, arg in enumerate(args) if list(params.keys())[i] != 'self'}, **kwargs} Tracing.tracers().start_span(span_name, inputs) try: - result = func(*args, **kwargs) + if inspect.iscoroutinefunction(func): + result = await func(*args, **kwargs) + else: + result = func(*args, **kwargs) if isinstance(result, dict): outputs = result + elif isinstance(result, BaseModel): + outputs = result.model_dump() else: outputs = {'output': result} Tracing.tracers().end_span(outputs) diff --git a/bpm-ai-core/bpm_ai_core/translation/amazon_translate.py b/bpm-ai-core/bpm_ai_core/translation/amazon_translate.py new file mode 100644 index 0000000..a817c64 --- /dev/null +++ b/bpm-ai-core/bpm_ai_core/translation/amazon_translate.py @@ -0,0 +1,38 @@ +import asyncio +from typing_extensions import override + +from typing import List + +from bpm_ai_core.translation.nmt import NMTModel + +try: + from aiobotocore.session import get_session + + has_amazon_translate = True +except ImportError: + has_amazon_translate = False + + +class AmazonTranslate(NMTModel): + """Amazon Translate NMT Model""" + def __init__(self, region_name: str = None): + if not has_amazon_translate: + raise ImportError("aiobotocore is not installed") + self.region_name = region_name + + async def _translate_single(self, text: str, target_language: str) -> str: + async with get_session().create_client('translate', region_name=self.region_name) as client: + response = await client.translate_text( + Text=text, + SourceLanguageCode='auto', + TargetLanguageCode=target_language + ) + return response['TranslatedText'] + + @override + async def _do_translate(self, text: str | List[str], target_language: str) -> str | List[str]: + if isinstance(text, str): + return await self._translate_single(text, target_language) + else: + tasks = [self._translate_single(t, target_language) for t in text] + return await asyncio.gather(*tasks) diff --git a/bpm-ai-core/bpm_ai_core/translation/azure_translation.py b/bpm-ai-core/bpm_ai_core/translation/azure_translation.py new file mode 100644 index 0000000..3b8577d --- /dev/null +++ b/bpm-ai-core/bpm_ai_core/translation/azure_translation.py @@ -0,0 +1,42 @@ +import logging +import os +from typing_extensions import override + +from bpm_ai_core.translation.nmt import NMTModel + +try: + from azure.ai.translation.text import TranslatorCredential + from azure.ai.translation.text.aio import TextTranslationClient + from azure.ai.translation.text.models import InputTextItem + + has_azure_translation = True +except ImportError: + has_azure_translation = False + +azure_logger = logging.getLogger('azure') +azure_logger.setLevel(logging.WARNING) + + +class AzureTranslation(NMTModel): + """Azure Text Translation NMT Model""" + + def __init__(self, endpoint: str = None, region: str = "westeurope"): + if not has_azure_translation: + raise ImportError('azure-ai-translation-text is not installed') + self.key = os.getenv('AZURE_TRANSLATION_KEY') + self.endpoint = endpoint or os.getenv('AZURE_TRANSLATION_ENDPOINT') + self.region = region or os.getenv('AZURE_TRANSLATION_REGION') + + @override + async def _do_translate(self, text: str | list[str], target_language: str) -> str | list[str]: + async with TextTranslationClient( + endpoint=self.endpoint, + credential=TranslatorCredential(self.key, self.region) + ) as client: + if isinstance(text, str): + texts = [text] + else: + texts = text + items = [InputTextItem(text=text) for text in texts] + response = await client.translate(content=items, to=[target_language]) + return [doc.translations[0].text for doc in response] diff --git a/bpm-ai-core/bpm_ai_core/translation/easy_nmt/easy_nmt.py b/bpm-ai-core/bpm_ai_core/translation/easy_nmt/easy_nmt.py index 4379911..e70a487 100644 --- a/bpm-ai-core/bpm_ai_core/translation/easy_nmt/easy_nmt.py +++ b/bpm-ai-core/bpm_ai_core/translation/easy_nmt/easy_nmt.py @@ -117,7 +117,7 @@ def __init__( self.translator = OpusMT(easynmt_path=model_path, **self.config['model_args']) self.translator.max_length = max_length - def _translate(self, text: str | list[str], target_language: str) -> str | list[str]: + async def _do_translate(self, text: str | list[str], target_language: str) -> str | list[str]: if isinstance(text, str): return self.do_translate(text, target_language, indentify_language(text)) else: @@ -356,13 +356,13 @@ def translate_stream(self, stream: Iterable[str], show_progress_bar: bool = True batch.append(doc) if len(batch) >= chunk_size: - translated = self.do_translate(batch, show_progress_bar=False, **kwargs) + translated = self._do_translate(batch, show_progress_bar=False, **kwargs) for trans_doc in translated: yield trans_doc batch = [] if len(batch) > 0: - translated = self.do_translate(batch, show_progress_bar=False, **kwargs) + translated = self._do_translate(batch, show_progress_bar=False, **kwargs) for trans_doc in translated: yield trans_doc diff --git a/bpm-ai-core/bpm_ai_core/translation/nmt.py b/bpm-ai-core/bpm_ai_core/translation/nmt.py index a3ea09f..17e7f20 100644 --- a/bpm-ai-core/bpm_ai_core/translation/nmt.py +++ b/bpm-ai-core/bpm_ai_core/translation/nmt.py @@ -1,5 +1,6 @@ from abc import ABC, abstractmethod +from bpm_ai_core.tracing.decorators import span from bpm_ai_core.tracing.tracing import Tracing @@ -9,14 +10,9 @@ class NMTModel(ABC): """ @abstractmethod - def _translate(self, text: str | list[str], target_language: str) -> str | list[str]: + async def _do_translate(self, text: str | list[str], target_language: str) -> str | list[str]: pass - def translate(self, text: str | list[str], target_language: str) -> str | list[str]: - Tracing.tracers().start_span("nmt", inputs={ - "text": text, - "target_language": target_language - }) - translation = self._translate(text, target_language) - Tracing.tracers().end_span(outputs={"translation": translation}) - return translation + @span(name="nmt") + async def translate(self, text: str | list[str], target_language: str) -> str | list[str]: + return await self._do_translate(text, target_language) diff --git a/bpm-ai-core/bpm_ai_core/util/file.py b/bpm-ai-core/bpm_ai_core/util/file.py index 1b100ce..c22cd0f 100644 --- a/bpm-ai-core/bpm_ai_core/util/file.py +++ b/bpm-ai-core/bpm_ai_core/util/file.py @@ -21,4 +21,4 @@ def is_supported_file(url_or_path: str, supported_extensions: List[str]) -> bool file_extension = file_extension.lower().lstrip('.') # Check if the file extension is in the list of supported extensions - return file_extension in supported_extensions \ No newline at end of file + return file_extension in supported_extensions diff --git a/bpm-ai-core/bpm_ai_core/util/image.py b/bpm-ai-core/bpm_ai_core/util/image.py index 29169db..919336d 100644 --- a/bpm-ai-core/bpm_ai_core/util/image.py +++ b/bpm-ai-core/bpm_ai_core/util/image.py @@ -1,14 +1,18 @@ import base64 -import os +import io +import logging import tempfile from io import BytesIO +from typing import Union, Tuple -import requests -from PIL import Image +from PIL import Image, ImageDraw from pdf2image import convert_from_path, convert_from_bytes +from bpm_ai_core.llm.common.blob import Blob from bpm_ai_core.util.file import is_supported_file +logger = logging.getLogger(__name__) + supported_img_extensions = [ 'bmp', 'dib', 'gif', @@ -28,33 +32,62 @@ def is_supported_img_file(url_or_path: str) -> bool: return is_supported_file(url_or_path, supported_extensions=supported_img_extensions) -def load_images(path: str) -> list[Image]: +async def blob_as_images(blob: Blob, accept_formats: list[str], return_bytes: bool = False) -> Union[list[Image.Image], list[bytes]]: """ - Load an image or pdf from a local path or a web URL into Pillow Image objects. + Load an image, PDF, or other file in a Blob into a Pillow Image object or raw bytes of accepted format. - Parameters: - - path (str): A file system path or a URL of an image or pdf. + Args: + blob: The input Blob object containing the image data. + accept_formats: A list of accepted image formats (e.g., ['png', 'jpeg']). + return_bytes: If True, return the image data as bytes instead of PIL Image objects. Returns: - - list[Image]: A list of PIL Images (single element for normal images, or an image for each pdf page). + A list of PIL Image objects or a list of bytes representing the converted images. """ - if path.startswith('http://') or path.startswith('https://'): - # Handle web URL - response = requests.get(path) - try: - images = [Image.open(BytesIO(response.content))] - except Exception: - images = pdf_to_images(response.content) - elif os.path.isfile(path): - # Handle local file path - try: - images = [Image.open(path)] - except Exception: - images = pdf_to_images(path) + if blob.is_pdf(): + # Convert PDF to a list of images + logger.info("Converting PDF to a list of images...") + images = pdf_to_images(await blob.as_bytes()) + elif blob.is_image(): + # Load the image from the blob + images = [Image.open(await blob.as_bytes_io())] else: - raise ValueError("The path provided is neither a valid URL nor a file path.") + raise ValueError(f"Unsupported blob type: {blob.mimetype}") + + accept_formats = [f.lower() for f in accept_formats] + + # Convert images to the accepted formats if necessary + converted_images = [] + for image in images: + if image.format.lower() not in accept_formats: + # Convert the image to the first accepted format + output_format = accept_formats[0] + logger.info(f"Converting images from {image.format.lower()} to accepted format: {output_format}") + if return_bytes: + # Convert the image to bytes + with io.BytesIO() as output_bytes: + if not image.mode == "RGB": + image = image.convert("RGB") + image.save(output_bytes, format=output_format) + converted_images.append(output_bytes.getvalue()) + else: + # Convert the image to PIL Image object + converted_image = io.BytesIO() + if not image.mode == "RGB": + image = image.convert("RGB") + image.save(converted_image, format=output_format) + converted_image.seek(0) + converted_images.append(Image.open(converted_image)) + else: + if return_bytes: + # Convert the image to bytes + with io.BytesIO() as output_bytes: + image.save(output_bytes, format=image.format) + converted_images.append(output_bytes.getvalue()) + else: + converted_images.append(image) - return images + return converted_images def pdf_to_images(pdf: bytes | str) -> list[Image]: @@ -80,3 +113,33 @@ def base64_encode_image(image: Image): image.save(buffered, format=image.format or "JPEG") # Assuming JPEG if format is not provided img_str = base64.b64encode(buffered.getvalue()) return img_str.decode('utf-8') + + +def draw_boxes_on_image(image: Image, normalized_boxes: list[Tuple[float, float, float, float]]): + """ + Draws bounding boxes on a given PIL image and displays the resulting image. + + Args: + image (PIL.Image): The input image. + normalized_boxes (list): A list of normalized bounding box coordinates. + Each box is represented as a tuple (x1, y1, x2, y2), + where (x1, y1) is the top-left corner and (x2, y2) is the bottom-right corner. + The coordinates are normalized, ranging from 0 to 1. + + Returns: + None + """ + draw = ImageDraw.Draw(image) + width, height = image.size + # Iterate over the normalized bounding boxes + for box in normalized_boxes: + x1, y1, x2, y2 = box + # Convert normalized coordinates to pixel coordinates + x1 = int(x1 * width) + y1 = int(y1 * height) + x2 = int(x2 * width) + y2 = int(y2 * height) + # Draw the bounding box rectangle + draw.rectangle(((x1, y1), (x2, y2)), outline="red", width=2) + # Display the image with bounding boxes + image.show() \ No newline at end of file diff --git a/bpm-ai-core/bpm_ai_core/util/json.py b/bpm-ai-core/bpm_ai_core/util/json_schema.py similarity index 100% rename from bpm-ai-core/bpm_ai_core/util/json.py rename to bpm-ai-core/bpm_ai_core/util/json_schema.py diff --git a/bpm-ai-core/bpm_ai_core/util/linguistics.py b/bpm-ai-core/bpm_ai_core/util/linguistics.py new file mode 100644 index 0000000..d8bd83d --- /dev/null +++ b/bpm-ai-core/bpm_ai_core/util/linguistics.py @@ -0,0 +1,186 @@ +stopwords = ['au', 'aux', 'avec', 'ce', 'ces', 'dans', 'de', 'des', 'du', 'elle', 'en', 'et', 'eux', 'il', 'ils', + 'je', 'la', 'le', 'les', 'leur', 'lui', 'ma', 'mais', 'me', 'même', 'mes', 'moi', 'mon', 'ne', 'nos', + 'notre', 'nous', 'on', 'ou', 'par', 'pas', 'pour', 'qu', 'que', 'qui', 'sa', 'se', 'ses', 'son', + 'sur', 'ta', 'te', 'tes', 'toi', 'ton', 'tu', 'un', 'une', 'vos', 'votre', 'vous', 'c', 'd', 'j', 'l', + 'à', 'm', 'n', 's', 't', 'y', 'été', 'étée', 'étées', 'étés', 'étant', 'étante', 'étants', 'étantes', + 'suis', 'es', 'est', 'sommes', 'êtes', 'sont', 'serai', 'seras', 'sera', 'serons', 'serez', 'seront', + 'serais', 'serait', 'serions', 'seriez', 'seraient', 'étais', 'était', 'étions', 'étiez', 'étaient', + 'fus', 'fut', 'fûmes', 'fûtes', 'furent', 'sois', 'soit', 'soyons', 'soyez', 'soient', 'fusse', + 'fusses', 'fût', 'fussions', 'fussiez', 'fussent', 'ayant', 'ayante', 'ayantes', 'ayants', 'eu', + 'eue', 'eues', 'eus', 'ai', 'as', 'avons', 'avez', 'ont', 'aurai', 'auras', 'aura', 'aurons', 'aurez', + 'auront', 'aurais', 'aurait', 'aurions', 'auriez', 'auraient', 'avais', 'avait', 'avions', 'aviez', + 'avaient', 'eut', 'eûmes', 'eûtes', 'eurent', 'aie', 'aies', 'ait', 'ayons', 'ayez', 'aient', 'eusse', + 'eusses', 'eût', 'eussions', 'eussiez', 'eussent', 'og', 'i', 'jeg', 'det', 'at', 'en', 'den', 'til', + 'er', 'som', 'på', 'de', 'med', 'han', 'af', 'for', 'ikke', 'der', 'var', 'mig', 'sig', 'men', 'et', + 'har', 'om', 'vi', 'min', 'havde', 'ham', 'hun', 'nu', 'over', 'da', 'fra', 'du', 'ud', 'sin', 'dem', + 'os', 'op', 'man', 'hans', 'hvor', 'eller', 'hvad', 'skal', 'selv', 'her', 'alle', 'vil', 'blev', + 'kunne', 'ind', 'når', 'være', 'dog', 'noget', 'ville', 'jo', 'deres', 'efter', 'ned', 'skulle', + 'denne', 'end', 'dette', 'mit', 'også', 'under', 'have', 'dig', 'anden', 'hende', 'mine', 'alt', + 'meget', 'sit', 'sine', 'vor', 'mod', 'disse', 'hvis', 'din', 'nogle', 'hos', 'blive', 'mange', 'ad', + 'bliver', 'hendes', 'været', 'thi', 'jer', 'sådan', 'olla', 'olen', 'olet', 'on', 'olemme', 'olette', + 'ovat', 'ole', 'oli', 'olisi', 'olisit', 'olisin', 'olisimme', 'olisitte', 'olisivat', 'olit', 'olin', + 'olimme', 'olitte', 'olivat', 'ollut', 'olleet', 'en', 'et', 'ei', 'emme', 'ette', 'eivät', 'minä', + 'minun', 'minut', 'minua', 'minussa', 'minusta', 'minuun', 'minulla', 'minulta', 'minulle', 'sinä', + 'sinun', 'sinut', 'sinua', 'sinussa', 'sinusta', 'sinuun', 'sinulla', 'sinulta', 'sinulle', 'hän', + 'hänen', 'hänet', 'häntä', 'hänessä', 'hänestä', 'häneen', 'hänellä', 'häneltä', 'hänelle', 'me', + 'meidän', 'meidät', 'meitä', 'meissä', 'meistä', 'meihin', 'meillä', 'meiltä', 'meille', 'te', + 'teidän', 'teidät', 'teitä', 'teissä', 'teistä', 'teihin', 'teillä', 'teiltä', 'teille', 'he', + 'heidän', 'heidät', 'heitä', 'heissä', 'heistä', 'heihin', 'heillä', 'heiltä', 'heille', 'tämä', + 'tämän', 'tätä', 'tässä', 'tästä', 'tähän', 'tallä', 'tältä', 'tälle', 'tänä', 'täksi', 'tuo', 'tuon', + 'tuotä', 'tuossa', 'tuosta', 'tuohon', 'tuolla', 'tuolta', 'tuolle', 'tuona', 'tuoksi', 'se', 'sen', + 'sitä', 'siinä', 'siitä', 'siihen', 'sillä', 'siltä', 'sille', 'sinä', 'siksi', 'nämä', 'näiden', + 'näitä', 'näissä', 'näistä', 'näihin', 'näillä', 'näiltä', 'näille', 'näinä', 'näiksi', 'nuo', + 'noiden', 'noita', 'noissa', 'noista', 'noihin', 'noilla', 'noilta', 'noille', 'noina', 'noiksi', + 'ne', 'niiden', 'niitä', 'niissä', 'niistä', 'niihin', 'niillä', 'niiltä', 'niille', 'niinä', + 'niiksi', 'kuka', 'kenen', 'kenet', 'ketä', 'kenessä', 'kenestä', 'keneen', 'kenellä', 'keneltä', + 'kenelle', 'kenenä', 'keneksi', 'ketkä', 'keiden', 'ketkä', 'keitä', 'keissä', 'keistä', 'keihin', + 'keillä', 'keiltä', 'keille', 'keinä', 'keiksi', 'mikä', 'minkä', 'minkä', 'mitä', 'missä', 'mistä', + 'mihin', 'millä', 'miltä', 'mille', 'minä', 'miksi', 'mitkä', 'joka', 'jonka', 'jota', 'jossa', + 'josta', 'johon', 'jolla', 'jolta', 'jolle', 'jona', 'joksi', 'jotka', 'joiden', 'joita', 'joissa', + 'joista', 'joihin', 'joilla', 'joilta', 'joille', 'joina', 'joiksi', 'että', 'ja', 'jos', 'koska', + 'kuin', 'mutta', 'niin', 'sekä', 'sillä', 'tai', 'vaan', 'vai', 'vaikka', 'kanssa', 'mukaan', 'noin', + 'poikki', 'yli', 'kun', 'niin', 'nyt', 'itse', 'a', 'à', 'ao', 'aos', 'aquela', 'aquelas', 'aquele', + 'aqueles', 'aquilo', 'as', 'às', 'até', 'com', 'como', 'da', 'das', 'de', 'dela', 'delas', 'dele', + 'deles', 'depois', 'do', 'dos', 'e', 'é', 'ela', 'elas', 'ele', 'eles', 'em', 'entre', 'era', 'eram', + 'éramos', 'essa', 'essas', 'esse', 'esses', 'esta', 'está', 'estamos', 'estão', 'estar', 'estas', + 'estava', 'estavam', 'estávamos', 'este', 'esteja', 'estejam', 'estejamos', 'estes', 'esteve', + 'estive', 'estivemos', 'estiver', 'estivera', 'estiveram', 'estivéramos', 'estiverem', 'estivermos', + 'estivesse', 'estivessem', 'estivéssemos', 'estou', 'eu', 'foi', 'fomos', 'for', 'fora', 'foram', + 'fôramos', 'forem', 'formos', 'fosse', 'fossem', 'fôssemos', 'fui', 'há', 'haja', 'hajam', 'hajamos', + 'hão', 'havemos', 'haver', 'hei', 'houve', 'houvemos', 'houver', 'houvera', 'houverá', 'houveram', + 'houvéramos', 'houverão', 'houverei', 'houverem', 'houveremos', 'houveria', 'houveriam', + 'houveríamos', 'houvermos', 'houvesse', 'houvessem', 'houvéssemos', 'isso', 'isto', 'já', 'lhe', + 'lhes', 'mais', 'mas', 'me', 'mesmo', 'meu', 'meus', 'minha', 'minhas', 'muito', 'na', 'não', 'nas', + 'nem', 'no', 'nos', 'nós', 'nossa', 'nossas', 'nosso', 'nossos', 'num', 'numa', 'o', 'os', 'ou', + 'para', 'pela', 'pelas', 'pelo', 'pelos', 'por', 'qual', 'quando', 'que', 'quem', 'são', 'se', 'seja', + 'sejam', 'sejamos', 'sem', 'ser', 'será', 'serão', 'serei', 'seremos', 'seria', 'seriam', 'seríamos', + 'seu', 'seus', 'só', 'somos', 'sou', 'sua', 'suas', 'também', 'te', 'tem', 'tém', 'temos', 'tenha', + 'tenham', 'tenhamos', 'tenho', 'terá', 'terão', 'terei', 'teremos', 'teria', 'teriam', 'teríamos', + 'teu', 'teus', 'teve', 'tinha', 'tinham', 'tínhamos', 'tive', 'tivemos', 'tiver', 'tivera', 'tiveram', + 'tivéramos', 'tiverem', 'tivermos', 'tivesse', 'tivessem', 'tivéssemos', 'tu', 'tua', 'tuas', 'um', + 'uma', 'você', 'vocês', 'vos', 'og', 'i', 'jeg', 'det', 'at', 'en', 'et', 'den', 'til', 'er', 'som', + 'på', 'de', 'med', 'han', 'av', 'ikke', 'ikkje', 'der', 'så', 'var', 'meg', 'seg', 'men', 'ett', + 'har', 'om', 'vi', 'min', 'mitt', 'ha', 'hadde', 'hun', 'nå', 'over', 'da', 'ved', 'fra', 'du', 'ut', + 'sin', 'dem', 'oss', 'opp', 'man', 'kan', 'hans', 'hvor', 'eller', 'hva', 'skal', 'selv', 'sjøl', + 'her', 'alle', 'vil', 'bli', 'ble', 'blei', 'blitt', 'kunne', 'inn', 'når', 'være', 'kom', 'noen', + 'noe', 'ville', 'dere', 'som', 'deres', 'kun', 'ja', 'etter', 'ned', 'skulle', 'denne', 'for', 'deg', + 'si', 'sine', 'sitt', 'mot', 'å', 'meget', 'hvorfor', 'dette', 'disse', 'uten', 'hvordan', 'ingen', + 'din', 'ditt', 'blir', 'samme', 'hvilken', 'hvilke', 'sånn', 'inni', 'mellom', 'vår', 'hver', 'hvem', + 'vors', 'hvis', 'både', 'bare', 'enn', 'fordi', 'før', 'mange', 'også', 'slik', 'vært', 'være', 'båe', + 'begge', 'siden', 'dykk', 'dykkar', 'dei', 'deira', 'deires', 'deim', 'di', 'då', 'eg', 'ein', 'eit', + 'eitt', 'elles', 'honom', 'hjå', 'ho', 'hoe', 'henne', 'hennar', 'hennes', 'hoss', 'hossen', 'ikkje', + 'ingi', 'inkje', 'korleis', 'korso', 'kva', 'kvar', 'kvarhelst', 'kven', 'kvi', 'kvifor', 'me', + 'medan', 'mi', 'mine', 'mykje', 'no', 'nokon', 'noka', 'nokor', 'noko', 'nokre', 'si', 'sia', 'sidan', + 'so', 'somt', 'somme', 'um', 'upp', 'vere', 'vore', 'verte', 'vort', 'varte', 'vart', 'i', 'me', 'my', + 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're", "you've", "you'll", "you'd", 'your', + 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', "she's", 'her', 'hers', + 'herself', 'it', "it's", 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', + 'which', 'who', 'whom', 'this', 'that', "that'll", 'these', 'those', 'am', 'is', 'are', 'was', 'were', + 'be', 'been', 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a', 'an', 'the', + 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for', 'with', 'about', + 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to', 'from', + 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'here', + 'there', 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', + 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', + 'can', 'will', 'just', 'don', "don't", 'should', "should've", 'now', 'd', 'll', 'm', 'o', 're', 've', + 'y', 'ain', 'aren', "aren't", 'couldn', "couldn't", 'didn', "didn't", 'doesn', "doesn't", 'hadn', + "hadn't", 'hasn', "hasn't", 'haven', "haven't", 'isn', "isn't", 'ma', 'mightn', "mightn't", 'mustn', + "mustn't", 'needn', "needn't", 'shan', "shan't", 'shouldn', "shouldn't", 'wasn', "wasn't", 'weren', + "weren't", 'won', "won't", 'wouldn', "wouldn't", 'ad', 'al', 'allo', 'ai', 'agli', 'all', 'agl', + 'alla', 'alle', 'con', 'col', 'coi', 'da', 'dal', 'dallo', 'dai', 'dagli', 'dall', 'dagl', 'dalla', + 'dalle', 'di', 'del', 'dello', 'dei', 'degli', 'dell', 'degl', 'della', 'delle', 'in', 'nel', 'nello', + 'nei', 'negli', 'nell', 'negl', 'nella', 'nelle', 'su', 'sul', 'sullo', 'sui', 'sugli', 'sull', + 'sugl', 'sulla', 'sulle', 'per', 'tra', 'contro', 'io', 'tu', 'lui', 'lei', 'noi', 'voi', 'loro', + 'mio', 'mia', 'miei', 'mie', 'tuo', 'tua', 'tuoi', 'tue', 'suo', 'sua', 'suoi', 'sue', 'nostro', + 'nostra', 'nostri', 'nostre', 'vostro', 'vostra', 'vostri', 'vostre', 'mi', 'ti', 'ci', 'vi', 'lo', + 'la', 'li', 'le', 'gli', 'ne', 'il', 'un', 'uno', 'una', 'ma', 'ed', 'se', 'perché', 'anche', 'come', + 'dov', 'dove', 'che', 'chi', 'cui', 'non', 'più', 'quale', 'quanto', 'quanti', 'quanta', 'quante', + 'quello', 'quelli', 'quella', 'quelle', 'questo', 'questi', 'questa', 'queste', 'si', 'tutto', + 'tutti', 'a', 'c', 'e', 'i', 'l', 'o', 'ho', 'hai', 'ha', 'abbiamo', 'avete', 'hanno', 'abbia', + 'abbiate', 'abbiano', 'avrò', 'avrai', 'avrà', 'avremo', 'avrete', 'avranno', 'avrei', 'avresti', + 'avrebbe', 'avremmo', 'avreste', 'avrebbero', 'avevo', 'avevi', 'aveva', 'avevamo', 'avevate', + 'avevano', 'ebbi', 'avesti', 'ebbe', 'avemmo', 'aveste', 'ebbero', 'avessi', 'avesse', 'avessimo', + 'avessero', 'avendo', 'avuto', 'avuta', 'avuti', 'avute', 'sono', 'sei', 'è', 'siamo', 'siete', 'sia', + 'siate', 'siano', 'sarò', 'sarai', 'sarà', 'saremo', 'sarete', 'saranno', 'sarei', 'saresti', + 'sarebbe', 'saremmo', 'sareste', 'sarebbero', 'ero', 'eri', 'era', 'eravamo', 'eravate', 'erano', + 'fui', 'fosti', 'fu', 'fummo', 'foste', 'furono', 'fossi', 'fosse', 'fossimo', 'fossero', 'essendo', + 'faccio', 'fai', 'facciamo', 'fanno', 'faccia', 'facciate', 'facciano', 'farò', 'farai', 'farà', + 'faremo', 'farete', 'faranno', 'farei', 'faresti', 'farebbe', 'faremmo', 'fareste', 'farebbero', + 'facevo', 'facevi', 'faceva', 'facevamo', 'facevate', 'facevano', 'feci', 'facesti', 'fece', + 'facemmo', 'faceste', 'fecero', 'facessi', 'facesse', 'facessimo', 'facessero', 'facendo', 'sto', + 'stai', 'sta', 'stiamo', 'stanno', 'stia', 'stiate', 'stiano', 'starò', 'starai', 'starà', 'staremo', + 'starete', 'staranno', 'starei', 'staresti', 'starebbe', 'staremmo', 'stareste', 'starebbero', + 'stavo', 'stavi', 'stava', 'stavamo', 'stavate', 'stavano', 'stetti', 'stesti', 'stette', 'stemmo', + 'steste', 'stettero', 'stessi', 'stesse', 'stessimo', 'stessero', 'stando', 'acaba', 'ama', 'aslında', + 'az', 'bazı', 'belki', 'biri', 'birkaç', 'birşey', 'biz', 'bu', 'çok', 'çünkü', 'da', 'daha', 'de', + 'defa', 'diye', 'eğer', 'en', 'gibi', 'hem', 'hep', 'hepsi', 'her', 'hiç', 'için', 'ile', 'ise', + 'kez', 'ki', 'kim', 'mı', 'mu', 'mü', 'nasıl', 'ne', 'neden', 'nerde', 'nerede', 'nereye', 'niçin', + 'niye', 'o', 'sanki', 'şey', 'siz', 'şu', 'tüm', 've', 'veya', 'ya', 'yani', 'aber', 'alle', 'allem', + 'allen', 'aller', 'alles', 'als', 'also', 'am', 'an', 'ander', 'andere', 'anderem', 'anderen', + 'anderer', 'anderes', 'anderm', 'andern', 'anderr', 'anders', 'auch', 'auf', 'aus', 'bei', 'bin', + 'bis', 'bist', 'da', 'damit', 'dann', 'der', 'den', 'des', 'dem', 'die', 'das', 'dass', 'daß', + 'derselbe', 'derselben', 'denselben', 'desselben', 'demselben', 'dieselbe', 'dieselben', 'dasselbe', + 'dazu', 'dein', 'deine', 'deinem', 'deinen', 'deiner', 'deines', 'denn', 'derer', 'dessen', 'dich', + 'dir', 'du', 'dies', 'diese', 'diesem', 'diesen', 'dieser', 'dieses', 'doch', 'dort', 'durch', 'ein', + 'eine', 'einem', 'einen', 'einer', 'eines', 'einig', 'einige', 'einigem', 'einigen', 'einiger', + 'einiges', 'einmal', 'er', 'ihn', 'ihm', 'es', 'etwas', 'euer', 'eure', 'eurem', 'euren', 'eurer', + 'eures', 'für', 'gegen', 'gewesen', 'hab', 'habe', 'haben', 'hat', 'hatte', 'hatten', 'hier', 'hin', + 'hinter', 'ich', 'mich', 'mir', 'ihr', 'ihre', 'ihrem', 'ihren', 'ihrer', 'ihres', 'euch', 'im', 'in', + 'indem', 'ins', 'ist', 'jede', 'jedem', 'jeden', 'jeder', 'jedes', 'jene', 'jenem', 'jenen', 'jener', + 'jenes', 'jetzt', 'kann', 'kein', 'keine', 'keinem', 'keinen', 'keiner', 'keines', 'können', 'könnte', + 'machen', 'man', 'manche', 'manchem', 'manchen', 'mancher', 'manches', 'mein', 'meine', 'meinem', + 'meinen', 'meiner', 'meines', 'mit', 'muss', 'musste', 'nach', 'nicht', 'nichts', 'noch', 'nun', + 'nur', 'ob', 'oder', 'ohne', 'sehr', 'sein', 'seine', 'seinem', 'seinen', 'seiner', 'seines', + 'selbst', 'sich', 'sie', 'ihnen', 'sind', 'so', 'solche', 'solchem', 'solchen', 'solcher', 'solches', + 'soll', 'sollte', 'sondern', 'sonst', 'über', 'um', 'und', 'uns', 'unsere', 'unserem', 'unseren', + 'unser', 'unseres', 'unter', 'viel', 'vom', 'von', 'vor', 'während', 'war', 'waren', 'warst', 'was', + 'weg', 'weil', 'weiter', 'welche', 'welchem', 'welchen', 'welcher', 'welches', 'wenn', 'werde', + 'werden', 'wie', 'wieder', 'will', 'wir', 'wird', 'wirst', 'wo', 'wollen', 'wollte', 'würde', + 'würden', 'zu', 'zum', 'zur', 'zwar', 'zwischen', 'de', 'en', 'van', 'ik', 'te', 'dat', 'die', 'in', + 'een', 'hij', 'het', 'niet', 'zijn', 'is', 'was', 'op', 'aan', 'met', 'als', 'voor', 'had', 'er', + 'maar', 'om', 'hem', 'dan', 'zou', 'of', 'wat', 'mijn', 'men', 'dit', 'zo', 'door', 'over', 'ze', + 'zich', 'bij', 'ook', 'tot', 'je', 'mij', 'uit', 'der', 'daar', 'haar', 'naar', 'heb', 'hoe', 'heeft', + 'hebben', 'deze', 'u', 'want', 'nog', 'zal', 'me', 'zij', 'nu', 'ge', 'geen', 'omdat', 'iets', + 'worden', 'toch', 'al', 'waren', 'veel', 'meer', 'doen', 'toen', 'moet', 'ben', 'zonder', 'kan', + 'hun', 'dus', 'alles', 'onder', 'ja', 'eens', 'hier', 'wie', 'werd', 'altijd', 'doch', 'wordt', + 'wezen', 'kunnen', 'ons', 'zelf', 'tegen', 'na', 'reeds', 'wil', 'kon', 'niets', 'uw', 'iemand', + 'geweest', 'andere', 'och', 'det', 'att', 'i', 'en', 'jag', 'hon', 'som', 'han', 'på', 'den', 'med', + 'var', 'sig', 'för', 'så', 'till', 'är', 'men', 'ett', 'om', 'hade', 'de', 'av', 'icke', 'mig', 'du', + 'henne', 'då', 'sin', 'nu', 'har', 'inte', 'hans', 'honom', 'skulle', 'hennes', 'där', 'min', 'man', + 'ej', 'vid', 'kunde', 'något', 'från', 'ut', 'när', 'efter', 'upp', 'vi', 'dem', 'vara', 'vad', + 'över', 'än', 'dig', 'kan', 'sina', 'här', 'ha', 'mot', 'alla', 'under', 'någon', 'eller', 'allt', + 'mycket', 'sedan', 'ju', 'denna', 'själv', 'detta', 'åt', 'utan', 'varit', 'hur', 'ingen', 'mitt', + 'ni', 'bli', 'blev', 'oss', 'din', 'dessa', 'några', 'deras', 'blir', 'mina', 'samma', 'vilken', 'er', + 'sådan', 'vår', 'blivit', 'dess', 'inom', 'mellan', 'sådant', 'varför', 'varje', 'vilka', 'ditt', + 'vem', 'vilket', 'sitta', 'sådana', 'vart', 'dina', 'vars', 'vårt', 'våra', 'ert', 'era', 'vilkas', + 'de', 'la', 'que', 'el', 'en', 'y', 'a', 'los', 'del', 'se', 'las', 'por', 'un', 'para', 'con', 'no', + 'una', 'su', 'al', 'lo', 'como', 'más', 'pero', 'sus', 'le', 'ya', 'o', 'este', 'sí', 'porque', + 'esta', 'entre', 'cuando', 'muy', 'sin', 'sobre', 'también', 'me', 'hasta', 'hay', 'donde', 'quien', + 'desde', 'todo', 'nos', 'durante', 'todos', 'uno', 'les', 'ni', 'contra', 'otros', 'ese', 'eso', + 'ante', 'ellos', 'e', 'esto', 'mí', 'antes', 'algunos', 'qué', 'unos', 'yo', 'otro', 'otras', 'otra', + 'él', 'tanto', 'esa', 'estos', 'mucho', 'quienes', 'nada', 'muchos', 'cual', 'poco', 'ella', 'estar', + 'estas', 'algunas', 'algo', 'nosotros', 'mi', 'mis', 'tú', 'te', 'ti', 'tu', 'tus', 'ellas', + 'nosotras', 'vosotros', 'vosotras', 'os', 'mío', 'mía', 'míos', 'mías', 'tuyo', 'tuya', 'tuyos', + 'tuyas', 'suyo', 'suya', 'suyos', 'suyas', 'nuestro', 'nuestra', 'nuestros', 'nuestras', 'vuestro', + 'vuestra', 'vuestros', 'vuestras', 'esos', 'esas', 'estoy', 'estás', 'está', 'estamos', 'estáis', + 'están', 'esté', 'estés', 'estemos', 'estéis', 'estén', 'estaré', 'estarás', 'estará', 'estaremos', + 'estaréis', 'estarán', 'estaría', 'estarías', 'estaríamos', 'estaríais', 'estarían', 'estaba', + 'estabas', 'estábamos', 'estabais', 'estaban', 'estuve', 'estuviste', 'estuvo', 'estuvimos', + 'estuvisteis', 'estuvieron', 'estuviera', 'estuvieras', 'estuviéramos', 'estuvierais', 'estuvieran', + 'estuviese', 'estuvieses', 'estuviésemos', 'estuvieseis', 'estuviesen', 'estando', 'estado', 'estada', + 'estados', 'estadas', 'estad', 'he', 'has', 'ha', 'hemos', 'habéis', 'han', 'haya', 'hayas', + 'hayamos', 'hayáis', 'hayan', 'habré', 'habrás', 'habrá', 'habremos', 'habréis', 'habrán', 'habría', + 'habrías', 'habríamos', 'habríais', 'habrían', 'había', 'habías', 'habíamos', 'habíais', 'habían', + 'hube', 'hubiste', 'hubo', 'hubimos', 'hubisteis', 'hubieron', 'hubiera', 'hubieras', 'hubiéramos', + 'hubierais', 'hubieran', 'hubiese', 'hubieses', 'hubiésemos', 'hubieseis', 'hubiesen', 'habiendo', + 'habido', 'habida', 'habidos', 'habidas', 'soy', 'eres', 'es', 'somos', 'sois', 'son', 'sea', 'seas', + 'seamos', 'seáis', 'sean', 'seré', 'serás', 'será', 'seremos', 'seréis', 'serán', 'sería', 'serías', + 'seríamos', 'seríais', 'serían', 'era', 'eras', 'éramos', 'erais', 'eran', 'fui', 'fuiste', 'fue', + 'fuimos', 'fuisteis', 'fueron', 'fuera', 'fueras', 'fuéramos', 'fuerais', 'fueran', 'fuese', 'fueses', + 'fuésemos', 'fueseis', 'fuesen', 'sintiendo', 'sentido', 'sentida', 'sentidos', 'sentidas', 'siente', + 'sentid', 'tengo', 'tienes', 'tiene', 'tenemos', 'tenéis', 'tienen', 'tenga', 'tengas', 'tengamos', + 'tengáis', 'tengan', 'tendré', 'tendrás', 'tendrá', 'tendremos', 'tendréis', 'tendrán', 'tendría', + 'tendrías', 'tendríamos', 'tendríais', 'tendrían', 'tenía', 'tenías', 'teníamos', 'teníais', 'tenían', + 'tuve', 'tuviste', 'tuvo', 'tuvimos', 'tuvisteis', 'tuvieron', 'tuviera', 'tuvieras', 'tuviéramos', + 'tuvierais', 'tuvieran', 'tuviese', 'tuvieses', 'tuviésemos', 'tuvieseis', 'tuviesen', 'teniendo', + 'tenido', 'tenida', 'tenidos', 'tenidas', 'tened'] diff --git a/bpm-ai-core/bpm_ai_core/util/storage.py b/bpm-ai-core/bpm_ai_core/util/storage.py new file mode 100644 index 0000000..61210bb --- /dev/null +++ b/bpm-ai-core/bpm_ai_core/util/storage.py @@ -0,0 +1,78 @@ +import os +from urllib.parse import urlparse + +try: + from aiobotocore.session import get_session +except ImportError: + pass + +try: + from azure.storage.blob.aio import BlobClient +except ImportError: + pass + + +def is_s3_url(url: str) -> bool: + return url.startswith('s3://') or (url.startswith('https://') and "amazonaws.com" in url) + + +async def read_file_from_s3(file_url: str) -> bytes: + """ + Reads a file from an S3 bucket using a URL-like format. + + Args: + file_url (str): The URL-like string representing the S3 file location. + Formats: + - "s3:///" + - "https://.s3..amazonaws.com/" + + Returns: + bytes: The contents of the file. + """ + try: + bucket_name, file_path = await parse_s3_url(file_url) + + async with get_session().create_client('s3') as s3: + response = await s3.get_object(Bucket=bucket_name, Key=file_path) + async with response['Body'] as stream: + return await stream.read() + except Exception as e: + raise Exception(f"Error reading file from S3: {str(e)}") + + +async def parse_s3_url(s3_url: str): + # Extract bucket name and file path based on the URL format + parsed_url = urlparse(s3_url) + if parsed_url.scheme == 's3': + bucket_name = parsed_url.netloc + file_path = parsed_url.path.lstrip('/') + elif parsed_url.scheme == 'https' and '.amazonaws.com' in parsed_url.netloc: + bucket_name = parsed_url.netloc.split('.')[0] + file_path = parsed_url.path.lstrip('/') + else: + raise ValueError("Invalid S3 file URL format.") + return bucket_name, file_path + + +def is_azure_blob_url(url: str) -> bool: + return "blob.core.windows.net" in url + + +async def read_file_from_azure_blob(file_url: str) -> bytes: + """ + Reads a file from Azure Blob Storage using a URL. + + Args: + file_url (str): The URL of the file in Azure Blob Storage. + Format: "https://.blob.core.windows.net//" + + Returns: + str: The contents of the file as a string. + """ + try: + access_key = os.environ.get('AZURE_STORAGE_ACCESS_KEY') + async with BlobClient.from_blob_url(file_url, credential=access_key) as blob_client: + blob = await blob_client.download_blob() + return await blob.readall() + except Exception as e: + raise Exception(f"Error reading file from Azure Blob Storage: {str(e)}") diff --git a/bpm-ai-core/poetry.lock b/bpm-ai-core/poetry.lock index cd47b4c..8d5cb1a 100644 --- a/bpm-ai-core/poetry.lock +++ b/bpm-ai-core/poetry.lock @@ -1,5 +1,178 @@ # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +[[package]] +name = "aiobotocore" +version = "2.12.1" +description = "Async client for aws services using botocore and aiohttp" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiobotocore-2.12.1-py3-none-any.whl", hash = "sha256:6a9a3d646cf422f45fdc1e4256e78563ebffba64733bc9b8ca9123614e8ba9af"}, + {file = "aiobotocore-2.12.1.tar.gz", hash = "sha256:8706b28f16f93c541f6ed50352115a79d8f3499539f8d0bb70aa0f7a5379c1fe"}, +] + +[package.dependencies] +aiohttp = ">=3.7.4.post0,<4.0.0" +aioitertools = ">=0.5.1,<1.0.0" +botocore = ">=1.34.41,<1.34.52" +wrapt = ">=1.10.10,<2.0.0" + +[package.extras] +awscli = ["awscli (>=1.32.41,<1.32.52)"] +boto3 = ["boto3 (>=1.34.41,<1.34.52)"] + +[[package]] +name = "aiohttp" +version = "3.9.3" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, + {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, + {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, + {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, + {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, + {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, + {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, + {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, + {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, + {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, + {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, + {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +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 = "aioitertools" +version = "0.11.0" +description = "itertools and builtins for AsyncIO and mixed iterables" +optional = false +python-versions = ">=3.6" +files = [ + {file = "aioitertools-0.11.0-py3-none-any.whl", hash = "sha256:04b95e3dab25b449def24d7df809411c10e62aab0cbe31a50ca4e68748c43394"}, + {file = "aioitertools-0.11.0.tar.gz", hash = "sha256:42c68b8dd3a69c2bf7f2233bf7df4bb58b557bca5252ac02ed5187bbc67d6831"}, +] + +[[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 = "amazon-textract-prettyprinter" +version = "0.1.9" +description = "Amazon Textract Helper tools for pretty printing" +optional = false +python-versions = ">=3.6" +files = [ + {file = "amazon-textract-prettyprinter-0.1.9.tar.gz", hash = "sha256:ba2eaab308eb94c7d90c9f8cdd93737a4b1120f69ea6ed436425570b6a789b98"}, + {file = "amazon_textract_prettyprinter-0.1.9-py2.py3-none-any.whl", hash = "sha256:66614c2878c9971f554d9778bf7fe549bbe12bf00b5a503c3c407883d55f50d5"}, +] + +[package.dependencies] +amazon-textract-response-parser = ">=0.1,<0.2" +boto3 = ">=1,<2" +botocore = "*" +tabulate = ">=0.9,<0.10" + +[[package]] +name = "amazon-textract-response-parser" +version = "0.1.48" +description = "Easily parse JSON returned by Amazon Textract." +optional = false +python-versions = ">=3.8" +files = [ + {file = "amazon-textract-response-parser-0.1.48.tar.gz", hash = "sha256:256206ade08318569c0e66d842cb9c98862990a3bccaadcb060dad2d225e6234"}, + {file = "amazon_textract_response_parser-0.1.48-py2.py3-none-any.whl", hash = "sha256:3f56911cf58f6abc73822c4fb991b9a46b2816887a34f856f2ccbf1dd0d8651e"}, +] + +[package.dependencies] +boto3 = "*" +marshmallow = ">=3.14,<4" + [[package]] name = "annotated-types" version = "0.6.0" @@ -55,6 +228,25 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphin 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 = "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 = "av" version = "10.0.0" @@ -109,33 +301,37 @@ files = [ ] [[package]] -name = "azure-ai-formrecognizer" -version = "3.3.2" -description = "Microsoft Azure Form Recognizer Client Library for Python" +name = "azure-ai-documentintelligence" +version = "1.0.0b2" +description = "Microsoft Azure AI Document Intelligence Client Library for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "azure-ai-formrecognizer-3.3.2.tar.gz", hash = "sha256:064803e0885bbe0429d1d282fc400123a5fc7f3baebb7f6ce30456450c08085e"}, - {file = "azure_ai_formrecognizer-3.3.2-py3-none-any.whl", hash = "sha256:3ea6ab27536e05f7a52953c8884f9488b4015bfe8904c87a4b5a8961b0a73792"}, + {file = "azure-ai-documentintelligence-1.0.0b2.tar.gz", hash = "sha256:9f79ee5a9785079836a888cec97cb068ddfbecbc51393abbd99b30bd814c712f"}, + {file = "azure_ai_documentintelligence-1.0.0b2-py3-none-any.whl", hash = "sha256:021a883d90a6ec867646c634ae5bbffadf1142adf46c575669e806f8704fe7f6"}, ] [package.dependencies] -azure-common = ">=1.1,<2.0" -azure-core = ">=1.23.0,<2.0.0" -msrest = ">=0.6.21" -typing-extensions = ">=4.0.1" +azure-core = ">=1.30.0,<2.0.0" +isodate = ">=0.6.1,<1.0.0" +typing-extensions = ">=4.6.0" [[package]] -name = "azure-common" -version = "1.1.28" -description = "Microsoft Azure Client Library for Python (Common)" +name = "azure-ai-translation-text" +version = "1.0.0b1" +description = "Azure Text Translation Client Library for Python" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "azure-common-1.1.28.zip", hash = "sha256:4ac0cd3214e36b6a1b6a442686722a5d8cc449603aa833f3f0f40bda836704a3"}, - {file = "azure_common-1.1.28-py2.py3-none-any.whl", hash = "sha256:5c12d3dcf4ec20599ca6b0d3e09e86e146353d443e7fcc050c9a19c1f9df20ad"}, + {file = "azure-ai-translation-text-1.0.0b1.zip", hash = "sha256:2c9eeb292f669f7e4592903ba0a39c2ce5343f61c6db2a688f67be228846b4d5"}, + {file = "azure_ai_translation_text-1.0.0b1-py3-none-any.whl", hash = "sha256:af700c3cd3253100c563bcc26ecdc9ab993c85297e790f0c526e801335127a8d"}, ] +[package.dependencies] +azure-core = ">=1.24.0,<2.0.0" +msrest = ">=0.7.1" +typing-extensions = ">=4.3.0" + [[package]] name = "azure-core" version = "1.30.1" @@ -155,6 +351,26 @@ typing-extensions = ">=4.6.0" [package.extras] aio = ["aiohttp (>=3.0)"] +[[package]] +name = "azure-storage-blob" +version = "12.19.1" +description = "Microsoft Azure Blob Storage Client Library for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "azure-storage-blob-12.19.1.tar.gz", hash = "sha256:13e16ba42fc54ac2c7e8f976062173a5c82b9ec0594728e134aac372965a11b0"}, + {file = "azure_storage_blob-12.19.1-py3-none-any.whl", hash = "sha256:c5530dc51c21c9564e4eb706cd499befca8819b10dd89716d3fc90d747556243"}, +] + +[package.dependencies] +azure-core = ">=1.28.0,<2.0.0" +cryptography = ">=2.1.4" +isodate = ">=0.6.1" +typing-extensions = ">=4.3.0" + +[package.extras] +aio = ["azure-core[aio] (>=1.28.0,<2.0.0)"] + [[package]] name = "backoff" version = "2.2.1" @@ -212,6 +428,44 @@ files = [ [package.dependencies] numpy = {version = ">=1.19.0", markers = "python_version >= \"3.9\""} +[[package]] +name = "boto3" +version = "1.34.51" +description = "The AWS SDK for Python" +optional = false +python-versions = ">= 3.8" +files = [ + {file = "boto3-1.34.51-py3-none-any.whl", hash = "sha256:67732634dc7d0afda879bd9a5e2d0818a2c14a98bef766b95a3e253ea5104cb9"}, + {file = "boto3-1.34.51.tar.gz", hash = "sha256:2cd9463e738a184cbce8a6824027c22163c5f73e277a35ff5aa0fb0e845b4301"}, +] + +[package.dependencies] +botocore = ">=1.34.51,<1.35.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.10.0,<0.11.0" + +[package.extras] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] + +[[package]] +name = "botocore" +version = "1.34.51" +description = "Low-level, data-driven core of boto 3." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "botocore-1.34.51-py3-none-any.whl", hash = "sha256:01d5156247f991b3466a8404e3d7460a9ecbd9b214f9992d6ba797d9ddc6f120"}, + {file = "botocore-1.34.51.tar.gz", hash = "sha256:5086217442e67dd9de36ec7e87a0c663f76b7790d5fb6a12de565af95e87e319"}, +] + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = {version = ">=1.25.4,<2.1", markers = "python_version >= \"3.10\""} + +[package.extras] +crt = ["awscrt (==0.19.19)"] + [[package]] name = "catalogue" version = "2.0.10" @@ -234,6 +488,70 @@ files = [ {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + [[package]] name = "charset-normalizer" version = "3.3.2" @@ -418,6 +736,60 @@ files = [ pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<3.0.0" srsly = ">=2.4.0,<3.0.0" +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + [[package]] name = "ctranslate2" version = "3.24.0" @@ -499,17 +871,6 @@ files = [ {file = "cymem-2.0.8.tar.gz", hash = "sha256:8fb09d222e21dcf1c7e907dc85cf74501d4cea6c4ed4ac6c9e016f98fb59cbbf"}, ] -[[package]] -name = "diskcache" -version = "5.6.3" -description = "Disk Cache -- Disk and file backed persistent cache." -optional = false -python-versions = ">=3" -files = [ - {file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"}, - {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, -] - [[package]] name = "distro" version = "1.9.0" @@ -521,25 +882,6 @@ files = [ {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, ] -[[package]] -name = "fastapi" -version = "0.110.0" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fastapi-0.110.0-py3-none-any.whl", hash = "sha256:87a1f6fb632a218222c5984be540055346a8f5d8a68e8f6fb647b1dc9934de4b"}, - {file = "fastapi-0.110.0.tar.gz", hash = "sha256:266775f0dcc95af9d3ef39bad55cff525329a931d5fd51930aadd4f428bf7ff3"}, -] - -[package.dependencies] -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.36.3,<0.37.0" -typing-extensions = ">=4.8.0" - -[package.extras] -all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] - [[package]] name = "faster-whisper" version = "0.10.1" @@ -564,40 +906,126 @@ dev = ["black (==23.*)", "flake8 (==6.*)", "isort (==5.*)", "pytest (==7.*)"] [[package]] name = "filelock" -version = "3.13.1" +version = "3.13.3" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "filelock-3.13.3-py3-none-any.whl", hash = "sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb"}, + {file = "filelock-3.13.3.tar.gz", hash = "sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +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 = "flatbuffers" -version = "24.3.7" +version = "24.3.25" description = "The FlatBuffers serialization format for Python" optional = false python-versions = "*" files = [ - {file = "flatbuffers-24.3.7-py2.py3-none-any.whl", hash = "sha256:80c4f5dcad0ee76b7e349671a0d657f2fbba927a0244f88dd3f5ed6a3694e1fc"}, - {file = "flatbuffers-24.3.7.tar.gz", hash = "sha256:0895c22b9a6019ff2f4de2e5e2f7cd15914043e6e7033a94c0c6369422690f22"}, + {file = "flatbuffers-24.3.25-py2.py3-none-any.whl", hash = "sha256:8dbdec58f935f3765e4f7f3cf635ac3a77f83568138d6a2311f524ec96364812"}, + {file = "flatbuffers-24.3.25.tar.gz", hash = "sha256:de2ec5b203f21441716617f38443e0a8ebf3d25bf0d9c0bb0ce68fa00ad546a4"}, +] + +[[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.0" +version = "2024.3.1" description = "File-system specification" optional = false python-versions = ">=3.8" files = [ - {file = "fsspec-2024.3.0-py3-none-any.whl", hash = "sha256:779001bd0122c9c4975cf03827d5e86c3afb914a3ae27040f15d341ab506a693"}, - {file = "fsspec-2024.3.0.tar.gz", hash = "sha256:f13a130c0ed07e15c4e1aeb0472a823e9c426b0b5792a1f40d902b0a71972d43"}, + {file = "fsspec-2024.3.1-py3-none-any.whl", hash = "sha256:918d18d41bf73f0e2b261824baeb1b124bcf771767e3a26425cd7dec3332f512"}, + {file = "fsspec-2024.3.1.tar.gz", hash = "sha256:f39780e282d7d117ffb42bb96992f8a90795e4d0fb0f661a70ca39fe9c43ded9"}, ] [package.extras] @@ -682,13 +1110,13 @@ socks = ["socksio (==1.*)"] [[package]] name = "huggingface-hub" -version = "0.21.4" +version = "0.22.0" 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.21.4-py3-none-any.whl", hash = "sha256:df37c2c37fc6c82163cdd8a67ede261687d80d1e262526d6c0ce73b6b3630a7b"}, - {file = "huggingface_hub-0.21.4.tar.gz", hash = "sha256:e1f4968c93726565a80edf6dc309763c7b546d0cfe79aa221206034d50155531"}, + {file = "huggingface_hub-0.22.0-py3-none-any.whl", hash = "sha256:72dea96299751699180184c06a4689e54cbfacecb1a3d08ac7a269c884bb17c3"}, + {file = "huggingface_hub-0.22.0.tar.gz", hash = "sha256:304f1e235c68c0a9f58bced47f13d6df241a5b4e3678f4981aa1e4f4bce63f6d"}, ] [package.dependencies] @@ -701,15 +1129,16 @@ tqdm = ">=4.42.1" typing-extensions = ">=3.7.4.3" [package.extras] -all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "mypy (==1.5.1)", "numpy", "pydantic (>1.1,<2.0)", "pydantic (>1.1,<3.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.1.3)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +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", "mypy (==1.5.1)", "numpy", "pydantic (>1.1,<2.0)", "pydantic (>1.1,<3.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.1.3)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +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", "pydantic (>1.1,<2.0)", "pydantic (>1.1,<3.0)"] -quality = ["mypy (==1.5.1)", "ruff (>=0.1.3)"] +inference = ["aiohttp", "minijinja (>=1.0)"] +quality = ["mypy (==1.5.1)", "ruff (>=0.3.0)"] tensorflow = ["graphviz", "pydot", "tensorflow"] -testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "numpy", "pydantic (>1.1,<2.0)", "pydantic (>1.1,<3.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +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)"] @@ -780,6 +1209,17 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, +] + [[package]] name = "joblib" version = "1.3.2" @@ -807,13 +1247,13 @@ data = ["language-data (>=1.1,<2.0)"] [[package]] name = "langfuse" -version = "2.20.3" +version = "2.21.1" description = "A client library for accessing langfuse" optional = false -python-versions = ">=3.8.1,<4.0" +python-versions = "<4.0,>=3.8.1" files = [ - {file = "langfuse-2.20.3-py3-none-any.whl", hash = "sha256:256468201d8ba98be3d22a059f2df88863c8f5ba6bff3bde3519e1649d71fd75"}, - {file = "langfuse-2.20.3.tar.gz", hash = "sha256:939e96fc143356d60378dde211670651dfd19d7f6b967d09495d635ad41feada"}, + {file = "langfuse-2.21.1-py3-none-any.whl", hash = "sha256:5ef286823a4c9903e2120ad2bf0169a929d41789702535abc713e66a0d270f05"}, + {file = "langfuse-2.21.1.tar.gz", hash = "sha256:36494ea016784ac339a1a5375b88c33484e81668433956ead442d7a93c217078"}, ] [package.dependencies] @@ -891,33 +1331,6 @@ files = [ [package.extras] test = ["pytest (==7.4.3)"] -[[package]] -name = "llama-cpp-python" -version = "0.2.57" -description = "Python bindings for the llama.cpp library" -optional = false -python-versions = ">=3.8" -files = [ - {file = "llama_cpp_python-0.2.57.tar.gz", hash = "sha256:bd81dbc4bc03b7deca3be0496330705d4c53bba726f7c3a47d556c7ec2452304"}, -] - -[package.dependencies] -diskcache = ">=5.6.1" -fastapi = {version = ">=0.100.0", optional = true, markers = "extra == \"server\""} -jinja2 = ">=2.11.3" -numpy = ">=1.20.0" -pydantic-settings = {version = ">=2.0.1", optional = true, markers = "extra == \"server\""} -sse-starlette = {version = ">=1.6.1", optional = true, markers = "extra == \"server\""} -starlette-context = {version = ">=0.3.6,<0.4", optional = true, markers = "extra == \"server\""} -typing-extensions = ">=4.5.0" -uvicorn = {version = ">=0.22.0", optional = true, markers = "extra == \"server\""} - -[package.extras] -all = ["llama_cpp_python[dev,server,test]"] -dev = ["black (>=23.3.0)", "httpx (>=0.24.1)", "mkdocs (>=1.4.3)", "mkdocs-material (>=9.1.18)", "mkdocstrings[python] (>=0.22.0)", "pytest (>=7.4.0)", "twine (>=4.0.2)"] -server = ["fastapi (>=0.100.0)", "pydantic-settings (>=2.0.1)", "sse-starlette (>=1.6.1)", "starlette-context (>=0.3.6,<0.4)", "uvicorn (>=0.22.0)"] -test = ["httpx (>=0.24.1)", "pytest (>=7.4.0)", "scipy (>=1.10)"] - [[package]] name = "markupsafe" version = "2.1.5" @@ -987,6 +1400,25 @@ files = [ {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] +[[package]] +name = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + [[package]] name = "mistralai" version = "0.1.6" @@ -1043,6 +1475,105 @@ requests-oauthlib = ">=0.5.0" [package.extras] async = ["aiodns", "aiohttp (>=3.0)"] +[[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 = "murmurhash" version = "1.0.10" @@ -1374,13 +1905,13 @@ sympy = "*" [[package]] name = "openai" -version = "1.14.1" +version = "1.14.3" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.14.1-py3-none-any.whl", hash = "sha256:f9322b0bf3b82bbd06930fad535369a023f35a3a96d3ef0b827644a15d7aae97"}, - {file = "openai-1.14.1.tar.gz", hash = "sha256:1fab5dd623cdc0c7c6e7da5d8d11fa6900f94191c2dfb6510d7eac33195fa175"}, + {file = "openai-1.14.3-py3-none-any.whl", hash = "sha256:7a465994a7ccf677a110c6cc2ef9d86229bad42c060b585b67049aa749f3b774"}, + {file = "openai-1.14.3.tar.gz", hash = "sha256:37b514e9c0ff45383ec9b242abd0f7859b1080d4b54b61393ed341ecad1b8eb9"}, ] [package.dependencies] @@ -1719,52 +2250,63 @@ files = [ [[package]] name = "pyarrow" -version = "15.0.1" +version = "15.0.2" description = "Python library for Apache Arrow" optional = false python-versions = ">=3.8" files = [ - {file = "pyarrow-15.0.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:c2ddb3be5ea938c329a84171694fc230b241ce1b6b0ff1a0280509af51c375fa"}, - {file = "pyarrow-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7543ea88a0ff72f8e6baaf9bfdbec2c62aeabdbede9e4a571c71cc3bc43b6302"}, - {file = "pyarrow-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1519e218a6941fc074e4501088d891afcb2adf77c236e03c34babcf3d6a0d1c7"}, - {file = "pyarrow-15.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28cafa86e1944761970d3b3fc0411b14ff9b5c2b73cd22aaf470d7a3976335f5"}, - {file = "pyarrow-15.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:be5c3d463e33d03eab496e1af7916b1d44001c08f0f458ad27dc16093a020638"}, - {file = "pyarrow-15.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:47b1eda15d3aa3f49a07b1808648e1397e5dc6a80a30bf87faa8e2d02dad7ac3"}, - {file = "pyarrow-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e524a31be7db22deebbbcf242b189063ab9a7652c62471d296b31bc6e3cae77b"}, - {file = "pyarrow-15.0.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:a476fefe8bdd56122fb0d4881b785413e025858803cc1302d0d788d3522b374d"}, - {file = "pyarrow-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:309e6191be385f2e220586bfdb643f9bb21d7e1bc6dd0a6963dc538e347b2431"}, - {file = "pyarrow-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83bc586903dbeb4365cbc72b602f99f70b96c5882e5dfac5278813c7d624ca3c"}, - {file = "pyarrow-15.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e652daac6d8b05280cd2af31c0fb61a4490ec6a53dc01588014d9fa3fdbee9"}, - {file = "pyarrow-15.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:abad2e08652df153a72177ce20c897d083b0c4ebeec051239e2654ddf4d3c996"}, - {file = "pyarrow-15.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cde663352bc83ad75ba7b3206e049ca1a69809223942362a8649e37bd22f9e3b"}, - {file = "pyarrow-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:1b6e237dd7a08482a8b8f3f6512d258d2460f182931832a8c6ef3953203d31e1"}, - {file = "pyarrow-15.0.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:7bd167536ee23192760b8c731d39b7cfd37914c27fd4582335ffd08450ff799d"}, - {file = "pyarrow-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7c08bb31eb2984ba5c3747d375bb522e7e536b8b25b149c9cb5e1c49b0ccb736"}, - {file = "pyarrow-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0f9c1d630ed2524bd1ddf28ec92780a7b599fd54704cd653519f7ff5aec177a"}, - {file = "pyarrow-15.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5186048493395220550bca7b524420471aac2d77af831f584ce132680f55c3df"}, - {file = "pyarrow-15.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:31dc30c7ec8958da3a3d9f31d6c3630429b2091ede0ecd0d989fd6bec129f0e4"}, - {file = "pyarrow-15.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3f111a014fb8ac2297b43a74bf4495cc479a332908f7ee49cb7cbd50714cb0c1"}, - {file = "pyarrow-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a6d1f7c15d7f68f08490d0cb34611497c74285b8a6bbeab4ef3fc20117310983"}, - {file = "pyarrow-15.0.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:9ad931b996f51c2f978ed517b55cb3c6078272fb4ec579e3da5a8c14873b698d"}, - {file = "pyarrow-15.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:738f6b53ab1c2f66b2bde8a1d77e186aeaab702d849e0dfa1158c9e2c030add3"}, - {file = "pyarrow-15.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c1c3fc16bc74e33bf8f1e5a212938ed8d88e902f372c4dac6b5bad328567d2f"}, - {file = "pyarrow-15.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1fa92512128f6c1b8dde0468c1454dd70f3bff623970e370d52efd4d24fd0be"}, - {file = "pyarrow-15.0.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:b4157f307c202cbbdac147d9b07447a281fa8e63494f7fc85081da351ec6ace9"}, - {file = "pyarrow-15.0.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:b75e7da26f383787f80ad76143b44844ffa28648fcc7099a83df1538c078d2f2"}, - {file = "pyarrow-15.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:3a99eac76ae14096c209850935057b9e8ce97a78397c5cde8724674774f34e5d"}, - {file = "pyarrow-15.0.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:dd532d3177e031e9b2d2df19fd003d0cc0520d1747659fcabbd4d9bb87de508c"}, - {file = "pyarrow-15.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ce8c89848fd37e5313fc2ce601483038ee5566db96ba0808d5883b2e2e55dc53"}, - {file = "pyarrow-15.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:862eac5e5f3b6477f7a92b2f27e560e1f4e5e9edfca9ea9da8a7478bb4abd5ce"}, - {file = "pyarrow-15.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f0ea3a29cd5cb99bf14c1c4533eceaa00ea8fb580950fb5a89a5c771a994a4e"}, - {file = "pyarrow-15.0.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:bb902f780cfd624b2e8fd8501fadab17618fdb548532620ef3d91312aaf0888a"}, - {file = "pyarrow-15.0.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:4f87757f02735a6bb4ad2e1b98279ac45d53b748d5baf52401516413007c6999"}, - {file = "pyarrow-15.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:efd3816c7fbfcbd406ac0f69873cebb052effd7cdc153ae5836d1b00845845d7"}, - {file = "pyarrow-15.0.1.tar.gz", hash = "sha256:21d812548d39d490e0c6928a7c663f37b96bf764034123d4b4ab4530ecc757a9"}, + {file = "pyarrow-15.0.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:88b340f0a1d05b5ccc3d2d986279045655b1fe8e41aba6ca44ea28da0d1455d8"}, + {file = "pyarrow-15.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eaa8f96cecf32da508e6c7f69bb8401f03745c050c1dd42ec2596f2e98deecac"}, + {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23c6753ed4f6adb8461e7c383e418391b8d8453c5d67e17f416c3a5d5709afbd"}, + {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f639c059035011db8c0497e541a8a45d98a58dbe34dc8fadd0ef128f2cee46e5"}, + {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:290e36a59a0993e9a5224ed2fb3e53375770f07379a0ea03ee2fce2e6d30b423"}, + {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:06c2bb2a98bc792f040bef31ad3e9be6a63d0cb39189227c08a7d955db96816e"}, + {file = "pyarrow-15.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:f7a197f3670606a960ddc12adbe8075cea5f707ad7bf0dffa09637fdbb89f76c"}, + {file = "pyarrow-15.0.2-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:5f8bc839ea36b1f99984c78e06e7a06054693dc2af8920f6fb416b5bca9944e4"}, + {file = "pyarrow-15.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f5e81dfb4e519baa6b4c80410421528c214427e77ca0ea9461eb4097c328fa33"}, + {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a4f240852b302a7af4646c8bfe9950c4691a419847001178662a98915fd7ee7"}, + {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e7d9cfb5a1e648e172428c7a42b744610956f3b70f524aa3a6c02a448ba853e"}, + {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2d4f905209de70c0eb5b2de6763104d5a9a37430f137678edfb9a675bac9cd98"}, + {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:90adb99e8ce5f36fbecbbc422e7dcbcbed07d985eed6062e459e23f9e71fd197"}, + {file = "pyarrow-15.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:b116e7fd7889294cbd24eb90cd9bdd3850be3738d61297855a71ac3b8124ee38"}, + {file = "pyarrow-15.0.2-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:25335e6f1f07fdaa026a61c758ee7d19ce824a866b27bba744348fa73bb5a440"}, + {file = "pyarrow-15.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:90f19e976d9c3d8e73c80be84ddbe2f830b6304e4c576349d9360e335cd627fc"}, + {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a22366249bf5fd40ddacc4f03cd3160f2d7c247692945afb1899bab8a140ddfb"}, + {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2a335198f886b07e4b5ea16d08ee06557e07db54a8400cc0d03c7f6a22f785f"}, + {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e6d459c0c22f0b9c810a3917a1de3ee704b021a5fb8b3bacf968eece6df098f"}, + {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:033b7cad32198754d93465dcfb71d0ba7cb7cd5c9afd7052cab7214676eec38b"}, + {file = "pyarrow-15.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:29850d050379d6e8b5a693098f4de7fd6a2bea4365bfd073d7c57c57b95041ee"}, + {file = "pyarrow-15.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:7167107d7fb6dcadb375b4b691b7e316f4368f39f6f45405a05535d7ad5e5058"}, + {file = "pyarrow-15.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e85241b44cc3d365ef950432a1b3bd44ac54626f37b2e3a0cc89c20e45dfd8bf"}, + {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:248723e4ed3255fcd73edcecc209744d58a9ca852e4cf3d2577811b6d4b59818"}, + {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ff3bdfe6f1b81ca5b73b70a8d482d37a766433823e0c21e22d1d7dde76ca33f"}, + {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:f3d77463dee7e9f284ef42d341689b459a63ff2e75cee2b9302058d0d98fe142"}, + {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:8c1faf2482fb89766e79745670cbca04e7018497d85be9242d5350cba21357e1"}, + {file = "pyarrow-15.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:28f3016958a8e45a1069303a4a4f6a7d4910643fc08adb1e2e4a7ff056272ad3"}, + {file = "pyarrow-15.0.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:89722cb64286ab3d4daf168386f6968c126057b8c7ec3ef96302e81d8cdb8ae4"}, + {file = "pyarrow-15.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd0ba387705044b3ac77b1b317165c0498299b08261d8122c96051024f953cd5"}, + {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad2459bf1f22b6a5cdcc27ebfd99307d5526b62d217b984b9f5c974651398832"}, + {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58922e4bfece8b02abf7159f1f53a8f4d9f8e08f2d988109126c17c3bb261f22"}, + {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:adccc81d3dc0478ea0b498807b39a8d41628fa9210729b2f718b78cb997c7c91"}, + {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:8bd2baa5fe531571847983f36a30ddbf65261ef23e496862ece83bdceb70420d"}, + {file = "pyarrow-15.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6669799a1d4ca9da9c7e06ef48368320f5856f36f9a4dd31a11839dda3f6cc8c"}, + {file = "pyarrow-15.0.2.tar.gz", hash = "sha256:9c9bc803cb3b7bfacc1e96ffbfd923601065d9d3f911179d81e72d99fd74a3d9"}, ] [package.dependencies] numpy = ">=1.16.6,<2" +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + [[package]] name = "pydantic" version = "2.6.4" @@ -1875,25 +2417,6 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" -[[package]] -name = "pydantic-settings" -version = "2.2.1" -description = "Settings management using Pydantic" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_settings-2.2.1-py3-none-any.whl", hash = "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091"}, - {file = "pydantic_settings-2.2.1.tar.gz", hash = "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed"}, -] - -[package.dependencies] -pydantic = ">=2.3.0" -python-dotenv = ">=0.21.0" - -[package.extras] -toml = ["tomli (>=2.0.1)"] -yaml = ["pyyaml (>=6.0.1)"] - [[package]] name = "pyreadline3" version = "3.4.1" @@ -1942,13 +2465,13 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no [[package]] name = "pytest-asyncio" -version = "0.23.5.post1" +version = "0.23.6" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"}, - {file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"}, + {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"}, + {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"}, ] [package.dependencies] @@ -2197,13 +2720,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-oauthlib" -version = "1.4.0" +version = "2.0.0" description = "OAuthlib authentication support for Requests." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.4" files = [ - {file = "requests-oauthlib-1.4.0.tar.gz", hash = "sha256:acee623221e4a39abcbb919312c8ff04bd44e7e417087fb4bd5e2a2f53d5e79a"}, - {file = "requests_oauthlib-1.4.0-py2.py3-none-any.whl", hash = "sha256:7a3130d94a17520169e38db6c8d75f2c974643788465ecc2e4b36d288bf13033"}, + {file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"}, + {file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"}, ] [package.dependencies] @@ -2213,6 +2736,23 @@ requests = ">=2.0.0" [package.extras] rsa = ["oauthlib[signedtoken] (>=3.0.0)"] +[[package]] +name = "s3transfer" +version = "0.10.1" +description = "An Amazon S3 Transfer Manager" +optional = false +python-versions = ">= 3.8" +files = [ + {file = "s3transfer-0.10.1-py3-none-any.whl", hash = "sha256:ceb252b11bcf87080fb7850a224fb6e05c8a776bab8f2b64b7f25b969464839d"}, + {file = "s3transfer-0.10.1.tar.gz", hash = "sha256:5683916b4c724f799e600f41dd9e10a9ff19871bf87623cc8f491cb4f5fa0a19"}, +] + +[package.dependencies] +botocore = ">=1.33.2,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] + [[package]] name = "sacremoses" version = "0.1.1" @@ -2633,68 +3173,32 @@ files = [ catalogue = ">=2.0.3,<2.1.0" [[package]] -name = "sse-starlette" -version = "2.0.0" -description = "SSE plugin for Starlette" +name = "sympy" +version = "1.12" +description = "Computer algebra system (CAS) in Python" optional = false python-versions = ">=3.8" files = [ - {file = "sse_starlette-2.0.0-py3-none-any.whl", hash = "sha256:c4dd134302cb9708d47cae23c365fe0a089aa2a875d2f887ac80f235a9ee5744"}, - {file = "sse_starlette-2.0.0.tar.gz", hash = "sha256:0c43cc43aca4884c88c8416b65777c4de874cc4773e6458d3579c0a353dc2fb7"}, + {file = "sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5"}, + {file = "sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8"}, ] [package.dependencies] -anyio = "*" -starlette = "*" -uvicorn = "*" - -[package.extras] -examples = ["fastapi"] +mpmath = ">=0.19" [[package]] -name = "starlette" -version = "0.36.3" -description = "The little ASGI library that shines." +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"}, - {file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"}, + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, ] -[package.dependencies] -anyio = ">=3.4.0,<5" - [package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] - -[[package]] -name = "starlette-context" -version = "0.3.6" -description = "Middleware for Starlette that allows you to store and access the context data of a request. Can be used with logging so logs automatically use request headers such as x-request-id or x-correlation-id." -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "starlette_context-0.3.6-py3-none-any.whl", hash = "sha256:b14ce373fbb6895a2182a7104b9f63ba20c8db83444005fb9a844dd77ad9895c"}, - {file = "starlette_context-0.3.6.tar.gz", hash = "sha256:d361a36ba2d4acca3ab680f917b25e281533d725374752d47607a859041958cb"}, -] - -[package.dependencies] -starlette = "*" - -[[package]] -name = "sympy" -version = "1.12" -description = "Computer algebra system (CAS) in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5"}, - {file = "sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8"}, -] - -[package.dependencies] -mpmath = ">=0.19" +widechars = ["wcwidth"] [[package]] name = "tenacity" @@ -3060,13 +3564,13 @@ telegram = ["requests"] [[package]] name = "transformers" -version = "4.38.2" +version = "4.39.1" description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" optional = false python-versions = ">=3.8.0" files = [ - {file = "transformers-4.38.2-py3-none-any.whl", hash = "sha256:c4029cb9f01b3dd335e52f364c52d2b37c65b4c78e02e6a08b1919c5c928573e"}, - {file = "transformers-4.38.2.tar.gz", hash = "sha256:c5fc7ad682b8a50a48b2a4c05d4ea2de5567adb1bdd00053619dbe5960857dd5"}, + {file = "transformers-4.39.1-py3-none-any.whl", hash = "sha256:df167e08b27ab254044a38bb7c439461cd3916332205416e9b6b1592b517a1a5"}, + {file = "transformers-4.39.1.tar.gz", hash = "sha256:ab9c1e1912843b9976e6cc62b27cd5434284fc0dab465e1b660333acfa81c6bc"}, ] [package.dependencies] @@ -3151,13 +3655,13 @@ tutorials = ["matplotlib", "pandas", "tabulate", "torch"] [[package]] name = "typer" -version = "0.9.0" +version = "0.9.4" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.6" files = [ - {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, - {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, + {file = "typer-0.9.4-py3-none-any.whl", hash = "sha256:aa6c4a4e2329d868b80ecbaf16f807f2b54e192209d7ac9dd42691d63f7a54eb"}, + {file = "typer-0.9.4.tar.gz", hash = "sha256:f714c2d90afae3a7929fcd72a3abb08df305e1ff61719381384211c4070af57f"}, ] [package.dependencies] @@ -3168,7 +3672,7 @@ typing-extensions = ">=3.7.4.3" all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.971)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] [[package]] name = "typing-extensions" @@ -3194,39 +3698,21 @@ files = [ [[package]] name = "urllib3" -version = "2.2.1" +version = "2.0.7" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "uvicorn" -version = "0.28.0" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.8" -files = [ - {file = "uvicorn-0.28.0-py3-none-any.whl", hash = "sha256:6623abbbe6176204a4226e67607b4d52cc60ff62cda0ff177613645cefa2ece1"}, - {file = "uvicorn-0.28.0.tar.gz", hash = "sha256:cab4473b5d1eaeb5a0f6375ac4bc85007ffc75c3cc1768816d9e5d589857b067"}, -] - -[package.dependencies] -click = ">=7.0" -h11 = ">=0.8" - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - [[package]] name = "wasabi" version = "1.1.2" @@ -3350,7 +3836,110 @@ files = [ {file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"}, ] +[[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" + [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "80869432d21b8cb560da0fa486c16f584becaccfc33ffb2502b9d1c7655f76c7" +content-hash = "372f439fc6b9f4f44f05a8bc63021702c29b09b50a37d81e58f47088012f0193" diff --git a/bpm-ai-core/pyproject.toml b/bpm-ai-core/pyproject.toml index 922620d..e79c5ea 100644 --- a/bpm-ai-core/pyproject.toml +++ b/bpm-ai-core/pyproject.toml @@ -17,6 +17,7 @@ pillow = "^10.1.0" pdf2image = "^1.17.0" requests = "^2.31.0" xmltodict = "^0.13.0" +aiohttp = "^3.9.3" [tool.poetry.group.test.dependencies] @@ -30,10 +31,14 @@ anthropic = "^0.18.1" mistralai = "^0.1.3" langfuse = "^2.7.6" setuptools = "^68.2.2" -azure-ai-formrecognizer = "^3.3.2" faster-whisper = "^0.10.0" lingua-language-detector = "^2.0.2" pytesseract = "^0.3.10" +amazon-textract-prettyprinter = "^0.1.9" +aiobotocore = "^2.12.1" +azure-storage-blob = "^12.19.1" +azure-ai-translation-text = "^1.0.0b1" +azure-ai-documentintelligence = "^1.0.0b2" torch = [ { version = "=2.2.0", source="pypi", markers = "sys_platform == 'darwin'" }, { version = "=2.2.0+cpu", source = "torch-cpu", markers = "sys_platform != 'darwin'" }, @@ -46,7 +51,6 @@ spacy = [ { version = "=3.7.2", markers = "sys_platform != 'darwin'" }, { version = "=3.7.2", extras = ["apple"], markers = "sys_platform == 'darwin' and platform_machine == 'arm64'" }, ] -#llama-cpp-python = {extras = ["server"], version = "^0.2.56"} [[tool.poetry.source]] name = "torch-cpu" diff --git a/bpm-ai-core/tests/invoice-sample.pdf b/bpm-ai-core/tests/invoice-sample.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fc3d58a76b9507cb2831a04487beaadcdeebd15c GIT binary patch literal 43627 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�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 G4&*=CjAW|- literal 0 HcmV?d00001 diff --git a/bpm-ai-core/tests/test.prompt b/bpm-ai-core/tests/test.prompt index 7c7dd9d..bc2faa8 100644 --- a/bpm-ai-core/tests/test.prompt +++ b/bpm-ai-core/tests/test.prompt @@ -1,6 +1,6 @@ [# system #] You are a smart assistant. -[# image {{image_url}} #] +[# blob {{image_url}} #] Go! [# user #] @@ -34,5 +34,5 @@ That's that. [# user #] Here is an image: -[# image {{image_url}} #] +[# blob {{image_url}} #] {{task}} \ No newline at end of file diff --git a/bpm-ai-core/tests/test_blob.py b/bpm-ai-core/tests/test_blob.py new file mode 100644 index 0000000..ae092ad --- /dev/null +++ b/bpm-ai-core/tests/test_blob.py @@ -0,0 +1,19 @@ +from bpm_ai_core.llm.common.blob import Blob + + +def test_blob_path(): + blob = Blob.from_path_or_url('test.mp3') + + assert blob.path.endswith('/test.mp3') + assert blob.mimetype == 'audio/mpeg' + assert blob.is_audio() is True + assert blob.data is None + + +def test_blob_path2(): + blob = Blob.from_path_or_url('example.jpg') + + assert blob.path.endswith('/example.jpg') + assert blob.mimetype == 'image/jpeg' + assert blob.is_image() is True + assert blob.data is None diff --git a/bpm-ai-core/tests/test_docvqa.py b/bpm-ai-core/tests/test_docvqa.py index c11b6f7..a031642 100644 --- a/bpm-ai-core/tests/test_docvqa.py +++ b/bpm-ai-core/tests/test_docvqa.py @@ -1,13 +1,53 @@ +import os + +import pytest + +from bpm_ai_core.llm.common.blob import Blob +from bpm_ai_core.question_answering.amazon_textract_docvqa import AmazonTextractDocVQA +from bpm_ai_core.question_answering.azure_doc_intelligence_docvqa import AzureDocVQA from bpm_ai_core.question_answering.transformers_docvqa import TransformersDocVQA -from bpm_ai_core.util.image import load_images async def test_docvqa(): model = TransformersDocVQA() - image = load_images("sample-invoice.webp") question = "What is the total?" - text = model.answer(context=image[0], question=question) + result = await model.answer( + context_str_or_blob=Blob.from_path_or_url("sample-invoice.webp"), + question=question + ) + + assert "300" in result.answer + + +async def test_docvqa_textract(): + if not os.environ.get("AWS_ACCESS_KEY_ID"): + pytest.skip("no API key provided") + + model = AmazonTextractDocVQA() + + question = "total" + + result = await model.answer( + context_str_or_blob=Blob.from_path_or_url("sample-invoice.webp"), + question=question + ) + + assert "300" in result.answer + + +async def test_docvqa_azure(): + if not os.environ.get("AZURE_DOCUMENT_INTELLIGENCE_KEY"): + pytest.skip("no API key provided") + + model = AzureDocVQA() + + question = "What is the total on the invoice?" + + result = await model.answer( + context_str_or_blob=Blob.from_path_or_url("sample-invoice.webp"), + question=question + ) - assert "300" in text + assert "300" in result.answer diff --git a/bpm-ai-core/tests/test_image.py b/bpm-ai-core/tests/test_image.py new file mode 100644 index 0000000..37906fb --- /dev/null +++ b/bpm-ai-core/tests/test_image.py @@ -0,0 +1,32 @@ +from bpm_ai_core.llm.common.blob import Blob +from bpm_ai_core.util.image import blob_as_images + + +async def test_blob_to_image_no_conversion(): + blob = Blob.from_path_or_url('example.png') + images = await blob_as_images(blob, accept_formats=["jpeg", "png"]) + + assert images[0].format == "PNG" + + blob = Blob.from_path_or_url('example.jpg') + images = await blob_as_images(blob, accept_formats=["jpeg", "png"]) + + assert images[0].format == "JPEG" + + +async def test_blob_to_image_conversion(): + blob = Blob.from_path_or_url('example.png') + images = await blob_as_images(blob, accept_formats=["jpeg"]) + assert images[0].format == "JPEG" + + blob = Blob.from_path_or_url('example.jpg') + images = await blob_as_images(blob, accept_formats=["png"]) + assert images[0].format == "PNG" + + blob = Blob.from_path_or_url('sample-invoice.webp') + images = await blob_as_images(blob, accept_formats=["jpeg"]) + assert images[0].format == "JPEG" + + blob = Blob.from_path_or_url('invoice-sample.pdf') + images = await blob_as_images(blob, accept_formats=["jpeg"]) + assert images[0].format == "JPEG" diff --git a/bpm-ai-core/tests/test_json.py b/bpm-ai-core/tests/test_json.py index 69eb3b0..e575835 100644 --- a/bpm-ai-core/tests/test_json.py +++ b/bpm-ai-core/tests/test_json.py @@ -1,4 +1,4 @@ -from bpm_ai_core.util.json import expand_simplified_json_schema +from bpm_ai_core.util.json_schema import expand_simplified_json_schema def test_json_schema_1(): diff --git a/bpm-ai-core/tests/test_nmt.py b/bpm-ai-core/tests/test_nmt.py index 7518d81..ce94bca 100644 --- a/bpm-ai-core/tests/test_nmt.py +++ b/bpm-ai-core/tests/test_nmt.py @@ -1,34 +1,34 @@ from bpm_ai_core.translation.easy_nmt.easy_nmt import EasyNMT -def test_translate(): +async def test_translate(): given_text = "Das ist ein Test" target_language = "en" expected_translated = "This is a test" model = EasyNMT() - translated = model.translate(given_text, target_language) + translated = await model.translate(given_text, target_language) assert translated == expected_translated -def test_translate_same(): +async def test_translate_same(): given_text = "Das ist ein Test" target_language = "de" expected_translated = "Das ist ein Test" model = EasyNMT() - translated = model.translate(given_text, target_language) + translated = await model.translate(given_text, target_language) assert translated == expected_translated -def test_translate_multiple(): +async def test_translate_multiple(): given_texts = ["Das ist ein Test", "Un altro test"] target_language = "en" expected_translated = ["This is a test", "Another test"] model = EasyNMT() - translated = model.translate(given_texts, target_language) + translated = await model.translate(given_texts, target_language) assert translated == expected_translated diff --git a/bpm-ai-core/tests/test_ocr.py b/bpm-ai-core/tests/test_ocr.py deleted file mode 100644 index c62d38b..0000000 --- a/bpm-ai-core/tests/test_ocr.py +++ /dev/null @@ -1,91 +0,0 @@ -import os - -import pytest -import requests - -from bpm_ai_core.ocr.tesseract import TesseractOCR -from bpm_ai_core.util.image import load_images - - -async def test_tesseract_image(): - ocr = TesseractOCR() - - image = load_images("example.png") - text = await ocr.images_to_text(image) - - assert "example" in text - - -async def test_tesseract_pdf(): - ocr = TesseractOCR() - - image = load_images("dummy.pdf") - text = await ocr.images_to_text(image) - - assert "Dummy PDF file" in text - - -async def test_tesseract_bbox(): - from PIL import Image, ImageDraw - import pytesseract - - # Load the image from file - image = load_images("https://slicedinvoices.com/pdf/wordpress-pdf-invoice-plugin-sample.pdf")[0] - - # Use Tesseract to do OCR on the image - data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT) - - # Create a draw object - draw = ImageDraw.Draw(image) - - # For each word in the data, - # draw a box around it on the image - for i in range(len(data['text'])): - # If the word isn't empty, - if data['text'][i].strip(): - # Get the bounding box - x, y, w, h = data['left'][i], data['top'][i], data['width'][i], data['height'][i] - # Draw the box - draw.rectangle(((x, y), (x + w, y + h)), outline='red') - - # Save the image with the boxes - image.save('output.jpg') - - -@pytest.mark.skip -async def test_azure_bbox(): - from azure.ai.formrecognizer import FormRecognizerClient - from azure.core.credentials import AzureKeyCredential - from PIL import Image, ImageDraw - - # Set up the FormRecognizerClient - endpoint = "https://westeurope.api.cognitive.microsoft.com/" - key = os.environ.get("AZURE_DOCUMENT_AI_KEY") - form_recognizer_client = FormRecognizerClient(endpoint, AzureKeyCredential(key)) - - # Load the image from file - image_path = 'doc.png' - with open(image_path, "rb") as fd: - form = fd.read() - - # Use Azure Form Recognizer to do OCR on the image - poller = form_recognizer_client.begin_recognize_content(form) - result = poller.result() - - # Load the image again for drawing - image = Image.open(image_path) - - # Create a draw object - draw = ImageDraw.Draw(image) - - # For each line in the result, - for page in result: - for line in page.lines: - # Get the bounding box - x1, y1 = line.bounding_box[0] - x2, y2 = line.bounding_box[2] - # Draw the box - draw.rectangle(((x1, y1), (x2, y2)), outline='red') - - # Save the image with the boxes - image.save('output.png') \ No newline at end of file diff --git a/bpm-ai-core/tests/test_prompt.py b/bpm-ai-core/tests/test_prompt.py index c55592e..92d1732 100644 --- a/bpm-ai-core/tests/test_prompt.py +++ b/bpm-ai-core/tests/test_prompt.py @@ -2,6 +2,7 @@ from PIL.Image import Image +from bpm_ai_core.llm.common.blob import Blob from bpm_ai_core.prompt.prompt import Prompt from bpm_ai_core.llm.common.message import ToolResultMessage, AssistantMessage, SystemMessage, UserMessage @@ -18,13 +19,14 @@ def test_prompt_format(): # [# system #] # You are a smart assistant. - # [# image {{image_url}} #] + # [# blob {{image_url}} #] # Go! assert isinstance(messages[0], SystemMessage) assert messages[0].role == "system" assert isinstance(messages[0].content, list) assert messages[0].content[0] == "You are a smart assistant." - assert isinstance(messages[0].content[1], Image) + assert isinstance(messages[0].content[1], Blob) + assert messages[0].content[1].is_image() assert messages[0].content[2] == "Go!" # [# user #] @@ -92,13 +94,14 @@ def test_prompt_format(): # [# user #] # Here is an image: - # [# image {{image_url}} #] + # [# blob {{image_url}} #] # {{task}} assert isinstance(messages[8], UserMessage) assert messages[8].role == "user" assert isinstance(messages[8].content, list) assert messages[8].content[0] == "Here is an image:" - assert isinstance(messages[8].content[1], Image) + assert isinstance(messages[0].content[1], Blob) + assert messages[0].content[1].is_image() assert messages[8].content[2] == "Do the task" diff --git a/bpm-ai-core/tests/test_qa.py b/bpm-ai-core/tests/test_qa.py index 03f5a27..a65dda1 100644 --- a/bpm-ai-core/tests/test_qa.py +++ b/bpm-ai-core/tests/test_qa.py @@ -1,37 +1,35 @@ from bpm_ai_core.question_answering.transformers_qa import TransformersExtractiveQA from bpm_ai_core.ocr.tesseract import TesseractOCR -from bpm_ai_core.util.image import load_images -def test_qa(): +async def test_qa(): context = "My name is John and I live in Hawaii" question = "Where does John live?" expected = "Hawaii" qa = TransformersExtractiveQA() - actual = qa.answer(context, question) + actual = await qa.answer(context, question) - assert actual.strip() == expected + assert actual.answer.strip() == expected async def test_qa_ocr(): ocr = TesseractOCR() - images = load_images("dummy.pdf") - text = await ocr.images_to_text(images) + ocr_result = await ocr.process("dummy.pdf") question = "What kind of PDF is this?" qa = TransformersExtractiveQA() - actual = qa.answer(text, question) + actual = await qa.answer(ocr_result.full_text, question) - assert actual.strip() == "Dummy" + assert actual.answer.strip() == "Dummy" -def test_qa_unanswerable(): +async def test_qa_unanswerable(): context = "My name is John and I live in Hawaii" question = "How much is the fish?" qa = TransformersExtractiveQA() - actual = qa.answer(context, question, confidence_threshold=0.1) + actual = await qa.answer(context, question, confidence_threshold=0.1) assert actual is None diff --git a/bpm-ai-core/tests/test_speech.py b/bpm-ai-core/tests/test_speech.py index 2b2adf5..1c34857 100644 --- a/bpm-ai-core/tests/test_speech.py +++ b/bpm-ai-core/tests/test_speech.py @@ -1,5 +1,4 @@ from bpm_ai_core.speech_recognition.faster_whisper import FasterWhisperASR -from bpm_ai_core.speech_recognition.openai_whisper import OpenAIWhisperASR async def test_faster_whisper():