From 7b7576ad55c6ad6806a207af3fc4a57cf41f1cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9lio=20L=C3=BAcio?= <42224962+helioLJ@users.noreply.github.com> Date: Mon, 26 Aug 2024 22:52:59 -0300 Subject: [PATCH] Add Azure AI Studio as provider (#7549) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hélio Lúcio --- .../azure_ai_studio/__init__.py | 0 .../azure_ai_studio/_assets/icon_l_en.png | Bin 0 -> 21236 bytes .../azure_ai_studio/_assets/icon_s_en.png | Bin 0 -> 10541 bytes .../azure_ai_studio/azure_ai_studio.py | 17 + .../azure_ai_studio/azure_ai_studio.yaml | 65 +++ .../azure_ai_studio/llm/__init__.py | 0 .../azure_ai_studio/llm/llm.py | 334 +++++++++++++++ .../azure_ai_studio/rerank/__init__.py | 0 .../azure_ai_studio/rerank/rerank.py | 164 ++++++++ api/poetry.lock | 396 +++++++++++++++++- api/pyproject.toml | 2 + .../model_runtime/azure_ai_studio/__init__.py | 0 .../model_runtime/azure_ai_studio/test_llm.py | 113 +++++ .../azure_ai_studio/test_provider.py | 17 + .../azure_ai_studio/test_rerank.py | 50 +++ 15 files changed, 1157 insertions(+), 1 deletion(-) create mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/__init__.py create mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_l_en.png create mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_s_en.png create mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/azure_ai_studio.py create mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/azure_ai_studio.yaml create mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/llm/__init__.py create mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/llm/llm.py create mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/rerank/__init__.py create mode 100644 api/core/model_runtime/model_providers/azure_ai_studio/rerank/rerank.py create mode 100644 api/tests/integration_tests/model_runtime/azure_ai_studio/__init__.py create mode 100644 api/tests/integration_tests/model_runtime/azure_ai_studio/test_llm.py create mode 100644 api/tests/integration_tests/model_runtime/azure_ai_studio/test_provider.py create mode 100644 api/tests/integration_tests/model_runtime/azure_ai_studio/test_rerank.py diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/__init__.py b/api/core/model_runtime/model_providers/azure_ai_studio/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_l_en.png new file mode 100644 index 0000000000000000000000000000000000000000..4b941654a78c1593450d336c2d784d45179f0e3e GIT binary patch literal 21236 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 literal 0 HcmV?d00001 diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_s_en.png b/api/core/model_runtime/model_providers/azure_ai_studio/_assets/icon_s_en.png new file mode 100644 index 0000000000000000000000000000000000000000..ca3043dc8dcb19139d4d8c8f179f74ae397dc217 GIT binary patch literal 10541 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 bAwDr)k^d#K?0= None: + """ + Validate provider credentials + + if validate failed, raise exception + + :param credentials: provider credentials, credentials form defined in `provider_credential_schema`. + """ + pass diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/azure_ai_studio.yaml b/api/core/model_runtime/model_providers/azure_ai_studio/azure_ai_studio.yaml new file mode 100644 index 0000000000..9e17ba0884 --- /dev/null +++ b/api/core/model_runtime/model_providers/azure_ai_studio/azure_ai_studio.yaml @@ -0,0 +1,65 @@ +provider: azure_ai_studio +label: + zh_Hans: Azure AI Studio + en_US: Azure AI Studio +icon_small: + en_US: icon_s_en.png +icon_large: + en_US: icon_l_en.png +description: + en_US: Azure AI Studio + zh_Hans: Azure AI Studio +background: "#93c5fd" +help: + title: + en_US: How to deploy customized model on Azure AI Studio + zh_Hans: 如何在Azure AI Studio上的私有化部署的模型 + url: + en_US: https://learn.microsoft.com/en-us/azure/ai-studio/how-to/deploy-models + zh_Hans: https://learn.microsoft.com/zh-cn/azure/ai-studio/how-to/deploy-models +supported_model_types: + - llm + - rerank +configurate_methods: + - customizable-model +model_credential_schema: + model: + label: + en_US: Model Name + zh_Hans: 模型名称 + placeholder: + en_US: Enter your model name + zh_Hans: 输入模型名称 + credential_form_schemas: + - variable: endpoint + label: + en_US: Azure AI Studio Endpoint + type: text-input + required: true + placeholder: + zh_Hans: 请输入你的Azure AI Studio推理端点 + en_US: 'Enter your API Endpoint, eg: https://example.com' + - variable: api_key + required: true + label: + en_US: API Key + zh_Hans: API Key + type: secret-input + placeholder: + en_US: Enter your Azure AI Studio API Key + zh_Hans: 在此输入您的 Azure AI Studio API Key + show_on: + - variable: __model_type + value: llm + - variable: jwt_token + required: true + label: + en_US: JWT Token + zh_Hans: JWT令牌 + type: secret-input + placeholder: + en_US: Enter your Azure AI Studio JWT Token + zh_Hans: 在此输入您的 Azure AI Studio 推理 API Key + show_on: + - variable: __model_type + value: rerank diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/llm/__init__.py b/api/core/model_runtime/model_providers/azure_ai_studio/llm/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/llm/llm.py b/api/core/model_runtime/model_providers/azure_ai_studio/llm/llm.py new file mode 100644 index 0000000000..42eae6c1e5 --- /dev/null +++ b/api/core/model_runtime/model_providers/azure_ai_studio/llm/llm.py @@ -0,0 +1,334 @@ +import logging +from collections.abc import Generator +from typing import Any, Optional, Union + +from azure.ai.inference import ChatCompletionsClient +from azure.ai.inference.models import StreamingChatCompletionsUpdate +from azure.core.credentials import AzureKeyCredential +from azure.core.exceptions import ( + ClientAuthenticationError, + DecodeError, + DeserializationError, + HttpResponseError, + ResourceExistsError, + ResourceModifiedError, + ResourceNotFoundError, + ResourceNotModifiedError, + SerializationError, + ServiceRequestError, + ServiceResponseError, +) + +from core.model_runtime.callbacks.base_callback import Callback +from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage +from core.model_runtime.entities.message_entities import ( + AssistantPromptMessage, + PromptMessage, + PromptMessageTool, +) +from core.model_runtime.entities.model_entities import ( + AIModelEntity, + FetchFrom, + I18nObject, + ModelType, + ParameterRule, + ParameterType, +) +from core.model_runtime.errors.invoke import ( + InvokeAuthorizationError, + InvokeBadRequestError, + InvokeConnectionError, + InvokeError, + InvokeServerUnavailableError, +) +from core.model_runtime.errors.validate import CredentialsValidateFailedError +from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel + +logger = logging.getLogger(__name__) + + +class AzureAIStudioLargeLanguageModel(LargeLanguageModel): + """ + Model class for Azure AI Studio large language model. + """ + + client: Any = None + + from azure.ai.inference.models import StreamingChatCompletionsUpdate + + def _invoke( + self, + model: str, + credentials: dict, + prompt_messages: list[PromptMessage], + model_parameters: dict, + tools: Optional[list[PromptMessageTool]] = None, + stop: Optional[list[str]] = None, + stream: bool = True, + user: Optional[str] = None, + ) -> Union[LLMResult, Generator]: + """ + Invoke large language model + + :param model: model name + :param credentials: model credentials + :param prompt_messages: prompt messages + :param model_parameters: model parameters + :param tools: tools for tool calling + :param stop: stop words + :param stream: is stream response + :param user: unique user id + :return: full response or stream response chunk generator result + """ + + if not self.client: + endpoint = credentials.get("endpoint") + api_key = credentials.get("api_key") + self.client = ChatCompletionsClient(endpoint=endpoint, credential=AzureKeyCredential(api_key)) + + messages = [{"role": msg.role.value, "content": msg.content} for msg in prompt_messages] + + payload = { + "messages": messages, + "max_tokens": model_parameters.get("max_tokens", 4096), + "temperature": model_parameters.get("temperature", 0), + "top_p": model_parameters.get("top_p", 1), + "stream": stream, + } + + if stop: + payload["stop"] = stop + + if tools: + payload["tools"] = [tool.model_dump() for tool in tools] + + try: + response = self.client.complete(**payload) + + if stream: + return self._handle_stream_response(response, model, prompt_messages) + else: + return self._handle_non_stream_response(response, model, prompt_messages, credentials) + except Exception as e: + raise self._transform_invoke_error(e) + + def _handle_stream_response(self, response, model: str, prompt_messages: list[PromptMessage]) -> Generator: + for chunk in response: + if isinstance(chunk, StreamingChatCompletionsUpdate): + if chunk.choices: + delta = chunk.choices[0].delta + if delta.content: + yield LLMResultChunk( + model=model, + prompt_messages=prompt_messages, + delta=LLMResultChunkDelta( + index=0, + message=AssistantPromptMessage(content=delta.content, tool_calls=[]), + ), + ) + + def _handle_non_stream_response( + self, response, model: str, prompt_messages: list[PromptMessage], credentials: dict + ) -> LLMResult: + assistant_text = response.choices[0].message.content + assistant_prompt_message = AssistantPromptMessage(content=assistant_text) + usage = self._calc_response_usage( + model, credentials, response.usage.prompt_tokens, response.usage.completion_tokens + ) + result = LLMResult(model=model, prompt_messages=prompt_messages, message=assistant_prompt_message, usage=usage) + + if hasattr(response, "system_fingerprint"): + result.system_fingerprint = response.system_fingerprint + + return result + + def _invoke_result_generator( + self, + model: str, + result: Generator, + credentials: dict, + prompt_messages: list[PromptMessage], + model_parameters: dict, + tools: Optional[list[PromptMessageTool]] = None, + stop: Optional[list[str]] = None, + stream: bool = True, + user: Optional[str] = None, + callbacks: Optional[list[Callback]] = None, + ) -> Generator: + """ + Invoke result generator + + :param result: result generator + :return: result generator + """ + callbacks = callbacks or [] + prompt_message = AssistantPromptMessage(content="") + usage = None + system_fingerprint = None + real_model = model + + try: + for chunk in result: + if isinstance(chunk, dict): + content = chunk["choices"][0]["message"]["content"] + usage = chunk["usage"] + chunk = LLMResultChunk( + model=model, + prompt_messages=prompt_messages, + delta=LLMResultChunkDelta( + index=0, + message=AssistantPromptMessage(content=content, tool_calls=[]), + ), + system_fingerprint=chunk.get("system_fingerprint"), + ) + + yield chunk + + self._trigger_new_chunk_callbacks( + chunk=chunk, + model=model, + credentials=credentials, + prompt_messages=prompt_messages, + model_parameters=model_parameters, + tools=tools, + stop=stop, + stream=stream, + user=user, + callbacks=callbacks, + ) + + prompt_message.content += chunk.delta.message.content + real_model = chunk.model + if hasattr(chunk.delta, "usage"): + usage = chunk.delta.usage + + if chunk.system_fingerprint: + system_fingerprint = chunk.system_fingerprint + except Exception as e: + raise self._transform_invoke_error(e) + + self._trigger_after_invoke_callbacks( + model=model, + result=LLMResult( + model=real_model, + prompt_messages=prompt_messages, + message=prompt_message, + usage=usage if usage else LLMUsage.empty_usage(), + system_fingerprint=system_fingerprint, + ), + credentials=credentials, + prompt_messages=prompt_messages, + model_parameters=model_parameters, + tools=tools, + stop=stop, + stream=stream, + user=user, + callbacks=callbacks, + ) + + def get_num_tokens( + self, + model: str, + credentials: dict, + prompt_messages: list[PromptMessage], + tools: Optional[list[PromptMessageTool]] = None, + ) -> int: + """ + Get number of tokens for given prompt messages + + :param model: model name + :param credentials: model credentials + :param prompt_messages: prompt messages + :param tools: tools for tool calling + :return: + """ + # Implement token counting logic here + # Might need to use a tokenizer specific to the Azure AI Studio model + return 0 + + def validate_credentials(self, model: str, credentials: dict) -> None: + """ + Validate model credentials + + :param model: model name + :param credentials: model credentials + :return: + """ + try: + endpoint = credentials.get("endpoint") + api_key = credentials.get("api_key") + client = ChatCompletionsClient(endpoint=endpoint, credential=AzureKeyCredential(api_key)) + client.get_model_info() + except Exception as ex: + raise CredentialsValidateFailedError(str(ex)) + + @property + def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: + """ + Map model invoke error to unified error + The key is the error type thrown to the caller + The value is the error type thrown by the model, + which needs to be converted into a unified error type for the caller. + + :return: Invoke error mapping + """ + return { + InvokeConnectionError: [ + ServiceRequestError, + ], + InvokeServerUnavailableError: [ + ServiceResponseError, + ], + InvokeAuthorizationError: [ + ClientAuthenticationError, + ], + InvokeBadRequestError: [ + HttpResponseError, + DecodeError, + ResourceExistsError, + ResourceNotFoundError, + ResourceModifiedError, + ResourceNotModifiedError, + SerializationError, + DeserializationError, + ], + } + + def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: + """ + Used to define customizable model schema + """ + rules = [ + ParameterRule( + name="temperature", + type=ParameterType.FLOAT, + use_template="temperature", + label=I18nObject(zh_Hans="温度", en_US="Temperature"), + ), + ParameterRule( + name="top_p", + type=ParameterType.FLOAT, + use_template="top_p", + label=I18nObject(zh_Hans="Top P", en_US="Top P"), + ), + ParameterRule( + name="max_tokens", + type=ParameterType.INT, + use_template="max_tokens", + min=1, + default=512, + label=I18nObject(zh_Hans="最大生成长度", en_US="Max Tokens"), + ), + ] + + entity = AIModelEntity( + model=model, + label=I18nObject(en_US=model), + fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, + model_type=ModelType.LLM, + features=[], + model_properties={}, + parameter_rules=rules, + ) + + return entity diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/rerank/__init__.py b/api/core/model_runtime/model_providers/azure_ai_studio/rerank/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/core/model_runtime/model_providers/azure_ai_studio/rerank/rerank.py b/api/core/model_runtime/model_providers/azure_ai_studio/rerank/rerank.py new file mode 100644 index 0000000000..6ed7ab277c --- /dev/null +++ b/api/core/model_runtime/model_providers/azure_ai_studio/rerank/rerank.py @@ -0,0 +1,164 @@ +import json +import logging +import os +import ssl +import urllib.request +from typing import Optional + +from core.model_runtime.entities.common_entities import I18nObject +from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType +from core.model_runtime.entities.rerank_entities import RerankDocument, RerankResult +from core.model_runtime.errors.invoke import ( + InvokeAuthorizationError, + InvokeBadRequestError, + InvokeConnectionError, + InvokeError, + InvokeRateLimitError, + InvokeServerUnavailableError, +) +from core.model_runtime.errors.validate import CredentialsValidateFailedError +from core.model_runtime.model_providers.__base.rerank_model import RerankModel + +logger = logging.getLogger(__name__) + + +class AzureRerankModel(RerankModel): + """ + Model class for Azure AI Studio rerank model. + """ + + def _allow_self_signed_https(self, allowed): + # bypass the server certificate verification on client side + if allowed and not os.environ.get("PYTHONHTTPSVERIFY", "") and getattr(ssl, "_create_unverified_context", None): + ssl._create_default_https_context = ssl._create_unverified_context + + def _azure_rerank(self, query_input: str, docs: list[str], endpoint: str, api_key: str): + # self._allow_self_signed_https(True) # Enable if using self-signed certificate + + data = {"inputs": query_input, "docs": docs} + + body = json.dumps(data).encode("utf-8") + headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"} + + req = urllib.request.Request(endpoint, body, headers) + + try: + with urllib.request.urlopen(req) as response: + result = response.read() + return json.loads(result) + except urllib.error.HTTPError as error: + logger.error(f"The request failed with status code: {error.code}") + logger.error(error.info()) + logger.error(error.read().decode("utf8", "ignore")) + raise + + def _invoke( + self, + model: str, + credentials: dict, + query: str, + docs: list[str], + score_threshold: Optional[float] = None, + top_n: Optional[int] = None, + user: Optional[str] = None, + ) -> RerankResult: + """ + Invoke rerank model + + :param model: model name + :param credentials: model credentials + :param query: search query + :param docs: docs for reranking + :param score_threshold: score threshold + :param top_n: top n + :param user: unique user id + :return: rerank result + """ + try: + if len(docs) == 0: + return RerankResult(model=model, docs=[]) + + endpoint = credentials.get("endpoint") + api_key = credentials.get("jwt_token") + + if not endpoint or not api_key: + raise ValueError("Azure endpoint and API key must be provided in credentials") + + result = self._azure_rerank(query, docs, endpoint, api_key) + logger.info(f"Azure rerank result: {result}") + + rerank_documents = [] + for idx, (doc, score_dict) in enumerate(zip(docs, result)): + score = score_dict["score"] + rerank_document = RerankDocument(index=idx, text=doc, score=score) + + if score_threshold is None or score >= score_threshold: + rerank_documents.append(rerank_document) + + rerank_documents.sort(key=lambda x: x.score, reverse=True) + + if top_n: + rerank_documents = rerank_documents[:top_n] + + return RerankResult(model=model, docs=rerank_documents) + + except Exception as e: + logger.exception(f"Exception in Azure rerank: {e}") + raise + + def validate_credentials(self, model: str, credentials: dict) -> None: + """ + Validate model credentials + + :param model: model name + :param credentials: model credentials + :return: + """ + try: + self._invoke( + model=model, + credentials=credentials, + query="What is the capital of the United States?", + docs=[ + "Carson City is the capital city of the American state of Nevada. At the 2010 United States " + "Census, Carson City had a population of 55,274.", + "The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that " + "are a political division controlled by the United States. Its capital is Saipan.", + ], + score_threshold=0.8, + ) + except Exception as ex: + raise CredentialsValidateFailedError(str(ex)) + + @property + def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]: + """ + Map model invoke error to unified error + The key is the error type thrown to the caller + The value is the error type thrown by the model, + which needs to be converted into a unified error type for the caller. + + :return: Invoke error mapping + """ + return { + InvokeConnectionError: [urllib.error.URLError], + InvokeServerUnavailableError: [urllib.error.HTTPError], + InvokeRateLimitError: [InvokeRateLimitError], + InvokeAuthorizationError: [InvokeAuthorizationError], + InvokeBadRequestError: [InvokeBadRequestError, KeyError, ValueError, json.JSONDecodeError], + } + + def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: + """ + used to define customizable model schema + """ + entity = AIModelEntity( + model=model, + label=I18nObject(en_US=model), + fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, + model_type=ModelType.RERANK, + model_properties={}, + parameter_rules=[], + ) + + return entity diff --git a/api/poetry.lock b/api/poetry.lock index a273b3c4d9..224c0bfb8c 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -551,6 +551,69 @@ files = [ [package.dependencies] cryptography = "*" +[[package]] +name = "azure-ai-inference" +version = "1.0.0b3" +description = "Microsoft Azure Ai Inference Client Library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "azure-ai-inference-1.0.0b3.tar.gz", hash = "sha256:1e99dc74c3b335a457500311bbbadb348f54dc4c12252a93cb8ab78d6d217ff0"}, + {file = "azure_ai_inference-1.0.0b3-py3-none-any.whl", hash = "sha256:6734ca7334c809a170beb767f1f1455724ab3f006cb60045e42a833c0e764403"}, +] + +[package.dependencies] +azure-core = ">=1.30.0" +isodate = ">=0.6.1" +typing-extensions = ">=4.6.0" + +[[package]] +name = "azure-ai-ml" +version = "1.19.0" +description = "Microsoft Azure Machine Learning Client Library for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "azure-ai-ml-1.19.0.tar.gz", hash = "sha256:94bb1afbb0497e539ae75455fc4a51b6942b5b68b3a275727ecce6ceb250eff9"}, + {file = "azure_ai_ml-1.19.0-py3-none-any.whl", hash = "sha256:f0385af06efbeae1f83113613e45343508d1288fd2f05857619e7c7d4d4f5302"}, +] + +[package.dependencies] +azure-common = ">=1.1" +azure-core = ">=1.23.0" +azure-mgmt-core = ">=1.3.0" +azure-storage-blob = ">=12.10.0" +azure-storage-file-datalake = ">=12.2.0" +azure-storage-file-share = "*" +colorama = "*" +isodate = "*" +jsonschema = ">=4.0.0" +marshmallow = ">=3.5" +msrest = ">=0.6.18" +opencensus-ext-azure = "*" +opencensus-ext-logging = "*" +pydash = ">=6.0.0" +pyjwt = "*" +pyyaml = ">=5.1.0" +strictyaml = "*" +tqdm = "*" +typing-extensions = "*" + +[package.extras] +designer = ["mldesigner"] +mount = ["azureml-dataprep-rslex (>=2.22.0)"] + +[[package]] +name = "azure-common" +version = "1.1.28" +description = "Microsoft Azure Client Library for Python (Common)" +optional = false +python-versions = "*" +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"}, +] + [[package]] name = "azure-core" version = "1.30.2" @@ -587,6 +650,20 @@ cryptography = ">=2.5" msal = ">=1.24.0" msal-extensions = ">=0.3.0" +[[package]] +name = "azure-mgmt-core" +version = "1.4.0" +description = "Microsoft Azure Management Core Library for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "azure-mgmt-core-1.4.0.zip", hash = "sha256:d195208340094f98e5a6661b781cde6f6a051e79ce317caabd8ff97030a9b3ae"}, + {file = "azure_mgmt_core-1.4.0-py3-none-any.whl", hash = "sha256:81071675f186a585555ef01816f2774d49c1c9024cb76e5720c3c0f6b337bb7d"}, +] + +[package.dependencies] +azure-core = ">=1.26.2,<2.0.0" + [[package]] name = "azure-storage-blob" version = "12.13.0" @@ -603,6 +680,42 @@ azure-core = ">=1.23.1,<2.0.0" cryptography = ">=2.1.4" msrest = ">=0.6.21" +[[package]] +name = "azure-storage-file-datalake" +version = "12.8.0" +description = "Microsoft Azure File DataLake Storage Client Library for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "azure-storage-file-datalake-12.8.0.zip", hash = "sha256:12e6306e5efb5ca28e0ccd9fa79a2c61acd589866d6109fe5601b18509da92f4"}, + {file = "azure_storage_file_datalake-12.8.0-py3-none-any.whl", hash = "sha256:b6cf5733fe794bf3c866efbe3ce1941409e35b6b125028ac558b436bf90f2de7"}, +] + +[package.dependencies] +azure-core = ">=1.23.1,<2.0.0" +azure-storage-blob = ">=12.13.0,<13.0.0" +msrest = ">=0.6.21" + +[[package]] +name = "azure-storage-file-share" +version = "12.17.0" +description = "Microsoft Azure Azure File Share Storage Client Library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "azure-storage-file-share-12.17.0.tar.gz", hash = "sha256:f7b2c6cfc1b7cb80097a53b1ed2efa9e545b49a291430d369cdb49fafbc841d6"}, + {file = "azure_storage_file_share-12.17.0-py3-none-any.whl", hash = "sha256:c4652759a9d529bf08881bb53275bf38774bb643746b849d27c47118f9cf923d"}, +] + +[package.dependencies] +azure-core = ">=1.28.0" +cryptography = ">=2.1.4" +isodate = ">=0.6.1" +typing-extensions = ">=4.6.0" + +[package.extras] +aio = ["azure-core[aio] (>=1.28.0)"] + [[package]] name = "backoff" version = "2.2.1" @@ -3952,6 +4065,41 @@ files = [ [package.dependencies] ply = "*" +[[package]] +name = "jsonschema" +version = "4.23.0" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, + {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + [[package]] name = "kaleido" version = "0.2.1" @@ -5277,6 +5425,65 @@ typing-extensions = ">=4.7,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] +[[package]] +name = "opencensus" +version = "0.11.4" +description = "A stats collection and distributed tracing framework" +optional = false +python-versions = "*" +files = [ + {file = "opencensus-0.11.4-py2.py3-none-any.whl", hash = "sha256:a18487ce68bc19900336e0ff4655c5a116daf10c1b3685ece8d971bddad6a864"}, + {file = "opencensus-0.11.4.tar.gz", hash = "sha256:cbef87d8b8773064ab60e5c2a1ced58bbaa38a6d052c41aec224958ce544eff2"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.0.0,<3.0.0", markers = "python_version >= \"3.6\""} +opencensus-context = ">=0.1.3" +six = ">=1.16,<2.0" + +[[package]] +name = "opencensus-context" +version = "0.1.3" +description = "OpenCensus Runtime Context" +optional = false +python-versions = "*" +files = [ + {file = "opencensus-context-0.1.3.tar.gz", hash = "sha256:a03108c3c10d8c80bb5ddf5c8a1f033161fa61972a9917f9b9b3a18517f0088c"}, + {file = "opencensus_context-0.1.3-py2.py3-none-any.whl", hash = "sha256:073bb0590007af276853009fac7e4bab1d523c3f03baf4cb4511ca38967c6039"}, +] + +[[package]] +name = "opencensus-ext-azure" +version = "1.1.13" +description = "OpenCensus Azure Monitor Exporter" +optional = false +python-versions = "*" +files = [ + {file = "opencensus-ext-azure-1.1.13.tar.gz", hash = "sha256:aec30472177005379ba56a702a097d618c5f57558e1bb6676ec75f948130692a"}, + {file = "opencensus_ext_azure-1.1.13-py2.py3-none-any.whl", hash = "sha256:06001fac6f8588ba00726a3a7c6c7f2fc88bc8ad12a65afdca657923085393dd"}, +] + +[package.dependencies] +azure-core = ">=1.12.0,<2.0.0" +azure-identity = ">=1.5.0,<2.0.0" +opencensus = ">=0.11.4,<1.0.0" +psutil = ">=5.6.3" +requests = ">=2.19.0" + +[[package]] +name = "opencensus-ext-logging" +version = "0.1.1" +description = "OpenCensus logging Integration" +optional = false +python-versions = "*" +files = [ + {file = "opencensus-ext-logging-0.1.1.tar.gz", hash = "sha256:c203b70f034151dada529f543af330ba17aaffec27d8a5267d03c713eb1de334"}, + {file = "opencensus_ext_logging-0.1.1-py2.py3-none-any.whl", hash = "sha256:cfdaf5da5d8b195ff3d1af87a4066a6621a28046173f6be4b0b6caec4a3ca89f"}, +] + +[package.dependencies] +opencensus = ">=0.8.0,<1.0.0" + [[package]] name = "openpyxl" version = "3.1.5" @@ -6021,6 +6228,35 @@ files = [ {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, ] +[[package]] +name = "psutil" +version = "6.0.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, + {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, + {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, + {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, + {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, + {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, + {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, + {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, + {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, + {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + [[package]] name = "psycopg2-binary" version = "2.9.9" @@ -6403,6 +6639,23 @@ azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0 toml = ["tomli (>=2.0.1)"] yaml = ["pyyaml (>=6.0.1)"] +[[package]] +name = "pydash" +version = "8.0.3" +description = "The kitchen sink of Python utility libraries for doing \"stuff\" in a functional way. Based on the Lo-Dash Javascript library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydash-8.0.3-py3-none-any.whl", hash = "sha256:c16871476822ee6b59b87e206dd27888240eff50a7b4cd72a4b80b43b6b994d7"}, + {file = "pydash-8.0.3.tar.gz", hash = "sha256:1b27cd3da05b72f0e5ff786c523afd82af796936462e631ffd1b228d91f8b9aa"}, +] + +[package.dependencies] +typing-extensions = ">3.10,<4.6.0 || >4.6.0" + +[package.extras] +dev = ["build", "coverage", "furo", "invoke", "mypy", "pytest", "pytest-cov", "pytest-mypy-testing", "ruff", "sphinx", "sphinx-autodoc-typehints", "tox", "twine", "wheel"] + [[package]] name = "pygments" version = "2.18.0" @@ -7170,6 +7423,21 @@ hiredis = {version = ">1.0.0", optional = true, markers = "extra == \"hiredis\"" hiredis = ["hiredis (>1.0.0)"] ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] +[[package]] +name = "referencing" +version = "0.35.1" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + [[package]] name = "regex" version = "2024.7.24" @@ -7377,6 +7645,118 @@ pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "rpds-py" +version = "0.20.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, + {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, + {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, + {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, + {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, + {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, + {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, + {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, + {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, + {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, + {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, + {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, + {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, + {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, +] + [[package]] name = "rsa" version = "4.9" @@ -7987,6 +8367,20 @@ anyio = ">=3.4.0,<5" [package.extras] full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] +[[package]] +name = "strictyaml" +version = "1.7.3" +description = "Strict, typed YAML parser" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "strictyaml-1.7.3-py3-none-any.whl", hash = "sha256:fb5c8a4edb43bebb765959e420f9b3978d7f1af88c80606c03fb420888f5d1c7"}, + {file = "strictyaml-1.7.3.tar.gz", hash = "sha256:22f854a5fcab42b5ddba8030a0e4be51ca89af0267961c8d6cfa86395586c407"}, +] + +[package.dependencies] +python-dateutil = ">=2.6.0" + [[package]] name = "sympy" version = "1.13.2" @@ -9669,4 +10063,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "04f970820de691f40fc9fb30f5ff0618b0f1a04d3315b14467fb88e475fa1243" +content-hash = "0b912a7d500c4ff3c7f0c51877e55de70cec5317a990f9e882600e32a30a610e" diff --git a/api/pyproject.toml b/api/pyproject.toml index e2d6704e8a..1f5bafe591 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -188,6 +188,8 @@ zhipuai = "1.0.7" # Related transparent dependencies with pinned verion # required by main implementations ############################################################ +azure-ai-ml = "^1.19.0" +azure-ai-inference = "^1.0.0b3" volcengine-python-sdk = {extras = ["ark"], version = "^1.0.98"} [tool.poetry.group.indriect.dependencies] kaleido = "0.2.1" diff --git a/api/tests/integration_tests/model_runtime/azure_ai_studio/__init__.py b/api/tests/integration_tests/model_runtime/azure_ai_studio/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/tests/integration_tests/model_runtime/azure_ai_studio/test_llm.py b/api/tests/integration_tests/model_runtime/azure_ai_studio/test_llm.py new file mode 100644 index 0000000000..8655b43d8f --- /dev/null +++ b/api/tests/integration_tests/model_runtime/azure_ai_studio/test_llm.py @@ -0,0 +1,113 @@ +import os +from collections.abc import Generator + +import pytest + +from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta +from core.model_runtime.entities.message_entities import ( + AssistantPromptMessage, + ImagePromptMessageContent, + PromptMessageTool, + SystemPromptMessage, + TextPromptMessageContent, + UserPromptMessage, +) +from core.model_runtime.errors.validate import CredentialsValidateFailedError +from core.model_runtime.model_providers.azure_ai_studio.llm.llm import AzureAIStudioLargeLanguageModel +from tests.integration_tests.model_runtime.__mock.azure_ai_studio import setup_azure_ai_studio_mock + + +@pytest.mark.parametrize("setup_azure_ai_studio_mock", [["chat"]], indirect=True) +def test_validate_credentials(setup_azure_ai_studio_mock): + model = AzureAIStudioLargeLanguageModel() + + with pytest.raises(CredentialsValidateFailedError): + model.validate_credentials( + model="gpt-35-turbo", + credentials={"api_key": "invalid_key", "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE")}, + ) + + model.validate_credentials( + model="gpt-35-turbo", + credentials={ + "api_key": os.getenv("AZURE_AI_STUDIO_API_KEY"), + "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE"), + }, + ) + + +@pytest.mark.parametrize("setup_azure_ai_studio_mock", [["chat"]], indirect=True) +def test_invoke_model(setup_azure_ai_studio_mock): + model = AzureAIStudioLargeLanguageModel() + + result = model.invoke( + model="gpt-35-turbo", + credentials={ + "api_key": os.getenv("AZURE_AI_STUDIO_API_KEY"), + "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE"), + }, + prompt_messages=[ + SystemPromptMessage( + content="You are a helpful AI assistant.", + ), + UserPromptMessage(content="Hello World!"), + ], + model_parameters={"temperature": 0.0, "max_tokens": 100}, + stream=False, + user="abc-123", + ) + + assert isinstance(result, LLMResult) + assert len(result.message.content) > 0 + + +@pytest.mark.parametrize("setup_azure_ai_studio_mock", [["chat"]], indirect=True) +def test_invoke_stream_model(setup_azure_ai_studio_mock): + model = AzureAIStudioLargeLanguageModel() + + result = model.invoke( + model="gpt-35-turbo", + credentials={ + "api_key": os.getenv("AZURE_AI_STUDIO_API_KEY"), + "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE"), + }, + prompt_messages=[ + SystemPromptMessage( + content="You are a helpful AI assistant.", + ), + UserPromptMessage(content="Hello World!"), + ], + model_parameters={"temperature": 0.0, "max_tokens": 100}, + stream=True, + user="abc-123", + ) + + assert isinstance(result, Generator) + + for chunk in result: + assert isinstance(chunk, LLMResultChunk) + assert isinstance(chunk.delta, LLMResultChunkDelta) + assert isinstance(chunk.delta.message, AssistantPromptMessage) + if chunk.delta.finish_reason is not None: + assert chunk.delta.usage is not None + assert chunk.delta.usage.completion_tokens > 0 + + +def test_get_num_tokens(): + model = AzureAIStudioLargeLanguageModel() + + num_tokens = model.get_num_tokens( + model="gpt-35-turbo", + credentials={ + "api_key": os.getenv("AZURE_AI_STUDIO_API_KEY"), + "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE"), + }, + prompt_messages=[ + SystemPromptMessage( + content="You are a helpful AI assistant.", + ), + UserPromptMessage(content="Hello World!"), + ], + ) + + assert num_tokens == 21 diff --git a/api/tests/integration_tests/model_runtime/azure_ai_studio/test_provider.py b/api/tests/integration_tests/model_runtime/azure_ai_studio/test_provider.py new file mode 100644 index 0000000000..8afe38b09b --- /dev/null +++ b/api/tests/integration_tests/model_runtime/azure_ai_studio/test_provider.py @@ -0,0 +1,17 @@ +import os + +import pytest + +from core.model_runtime.errors.validate import CredentialsValidateFailedError +from core.model_runtime.model_providers.azure_ai_studio.azure_ai_studio import AzureAIStudioProvider + + +def test_validate_provider_credentials(): + provider = AzureAIStudioProvider() + + with pytest.raises(CredentialsValidateFailedError): + provider.validate_provider_credentials(credentials={}) + + provider.validate_provider_credentials( + credentials={"api_key": os.getenv("AZURE_AI_STUDIO_API_KEY"), "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE")} + ) diff --git a/api/tests/integration_tests/model_runtime/azure_ai_studio/test_rerank.py b/api/tests/integration_tests/model_runtime/azure_ai_studio/test_rerank.py new file mode 100644 index 0000000000..466facc5ff --- /dev/null +++ b/api/tests/integration_tests/model_runtime/azure_ai_studio/test_rerank.py @@ -0,0 +1,50 @@ +import os + +import pytest + +from core.model_runtime.entities.rerank_entities import RerankResult +from core.model_runtime.errors.validate import CredentialsValidateFailedError +from core.model_runtime.model_providers.azure_ai_studio.rerank.rerank import AzureAIStudioRerankModel + + +def test_validate_credentials(): + model = AzureAIStudioRerankModel() + + with pytest.raises(CredentialsValidateFailedError): + model.validate_credentials( + model="azure-ai-studio-rerank-v1", + credentials={"api_key": "invalid_key", "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE")}, + query="What is the capital of the United States?", + docs=[ + "Carson City is the capital city of the American state of Nevada. At the 2010 United States " + "Census, Carson City had a population of 55,274.", + "The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that " + "are a political division controlled by the United States. Its capital is Saipan.", + ], + score_threshold=0.8, + ) + + +def test_invoke_model(): + model = AzureAIStudioRerankModel() + + result = model.invoke( + model="azure-ai-studio-rerank-v1", + credentials={ + "api_key": os.getenv("AZURE_AI_STUDIO_JWT_TOKEN"), + "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE"), + }, + query="What is the capital of the United States?", + docs=[ + "Carson City is the capital city of the American state of Nevada. At the 2010 United States " + "Census, Carson City had a population of 55,274.", + "The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that " + "are a political division controlled by the United States. Its capital is Saipan.", + ], + score_threshold=0.8, + ) + + assert isinstance(result, RerankResult) + assert len(result.docs) == 1 + assert result.docs[0].index == 1 + assert result.docs[0].score >= 0.8