From 6998d31e4385750cd1a3e2175f952a99d98c7d3f Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Thu, 22 Sep 2022 15:13:09 -0700 Subject: [PATCH 1/5] add impl for dynamic classification --- .../azure-ai-textanalytics/CHANGELOG.md | 1 + .../azure-ai-textanalytics/README.md | 5 + .../azure/ai/textanalytics/__init__.py | 5 +- .../azure/ai/textanalytics/_models.py | 61 ++++++++++- .../ai/textanalytics/_response_handlers.py | 10 ++ .../textanalytics/_text_analytics_client.py | 101 ++++++++++++++++- .../aio/_text_analytics_client_async.py | 103 +++++++++++++++++- .../azure-ai-textanalytics/samples/README.md | 3 + 8 files changed, 284 insertions(+), 5 deletions(-) diff --git a/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md b/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md index 3a7f6b60a841..968e74e0deef 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md +++ b/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md @@ -3,6 +3,7 @@ ## 5.3.0b1 (Unreleased) ### Features Added +- Added `dynamically_classify` client method to dynamically classify documents without needing to train a model. ### Breaking Changes diff --git a/sdk/textanalytics/azure-ai-textanalytics/README.md b/sdk/textanalytics/azure-ai-textanalytics/README.md index ba63c1fb0789..cc9f654f6dbc 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/README.md +++ b/sdk/textanalytics/azure-ai-textanalytics/README.md @@ -12,6 +12,7 @@ The Azure Cognitive Service for Language is a cloud-based service that provides - Text Analytics for Health - Custom Named Entity Recognition - Custom Text Classification +- Dynamic Classification [Source code][source_code] | [Package (PyPI)][ta_pypi] | [API reference documentation][ta_ref_docs] | [Product documentation][language_product_documentation] | [Samples][ta_samples] @@ -254,6 +255,7 @@ The following section provides several code snippets covering some of the most c - [Custom Entity Recognition][recognize_custom_entities_sample] - [Custom Single Label Classification][single_label_classify_sample] - [Custom Multi Label Classification][multi_label_classify_sample] +- [Dynamic Classification][dynamically_classify_sample] ### Analyze sentiment @@ -664,6 +666,7 @@ Common scenarios - Custom Entity Recognition: [sample_recognize_custom_entities.py][recognize_custom_entities_sample] ([async_version][recognize_custom_entities_sample_async]) - Custom Single Label Classification: [sample_single_label_classify.py][single_label_classify_sample] ([async_version][single_label_classify_sample_async]) - Custom Multi Label Classification: [sample_multi_label_classify.py][multi_label_classify_sample] ([async_version][multi_label_classify_sample_async]) +- Dynamic Classification: [sample_dynamically_classify.py][dynamically_classify_sample] ([async_version][dynamically_classify_sample_async]) Advanced scenarios @@ -766,6 +769,8 @@ This project has adopted the [Microsoft Open Source Code of Conduct][code_of_con [multi_label_classify_sample]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_multi_label_classify.py [multi_label_classify_sample_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_multi_label_classify_async.py [healthcare_action_sample]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_healthcare_action.py +[dynamically_classify_sample]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamically_classify.py +[dynamically_classify_sample_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamically_classify_async.py [cla]: https://cla.microsoft.com [code_of_conduct]: https://opensource.microsoft.com/codeofconduct/ [coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py index d0254abbcdca..2090c2f8ada9 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py @@ -57,8 +57,9 @@ ClassificationCategory, AnalyzeHealthcareEntitiesAction, TextAnalysisKind, + DynamicClassificationResult ) - +from ._generated import ClassificationType from ._lro import AnalyzeHealthcareEntitiesLROPoller, AnalyzeActionsLROPoller, TextAnalysisLROPoller __all__ = [ @@ -117,6 +118,8 @@ "AnalyzeHealthcareEntitiesAction", "TextAnalysisLROPoller", "TextAnalysisKind", + "ClassificationType", + "DynamicClassificationResult", ] __version__ = VERSION diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py index b86eea745445..629fcfb0d33f 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py @@ -2480,7 +2480,7 @@ def _to_generated(self, api_version, task_id): # pylint: disable=unused-argumen class ClassificationCategory(DictMixin): """ClassificationCategory represents a classification of the input document. - :ivar str category: Custom classification category for the document. + :ivar str category: Classification category for the document. :ivar float confidence_score: Confidence score between 0 and 1 of the recognized classification. """ @@ -2567,3 +2567,62 @@ def _to_generated(self, api_version, task_id): # pylint: disable=unused-argumen logging_opt_out=self.disable_service_logs, ) ) + +class DynamicClassificationResult(DictMixin): + """DynamicClassificationResult is a result object which contains + the classifications for a particular document. + + :ivar str id: Unique, non-empty document identifier. + :ivar classifications: Recognized classification results in the document. + :vartype classifications: list[~azure.ai.textanalytics.ClassificationCategory] + :ivar warnings: Warnings encountered while processing document. + :vartype warnings: list[~azure.ai.textanalytics.TextAnalyticsWarning] + :ivar statistics: If `show_stats=True` was specified in the request this + field will contain information about the document payload. + :vartype statistics: Optional[~azure.ai.textanalytics.TextDocumentStatistics] + :ivar bool is_error: Boolean check for error item when iterating over list of + results. Always False for an instance of a DynamicClassificationResult. + :ivar str kind: The text analysis kind - "DynamicClassification". + """ + + def __init__( + self, + **kwargs + ): + self.id = kwargs.get('id', None) + self.classifications = kwargs.get('classifications', None) + self.warnings = kwargs.get('warnings', []) + self.statistics = kwargs.get('statistics', None) + self.is_error: Literal[False] = False + self.kind: Literal["DynamicClassification"] = "DynamicClassification" + + def __repr__(self): + return "DynamicClassificationResult(id={}, classifications={}, warnings={}, statistics={}, " \ + "is_error={})".format( + self.id, + repr(self.classifications), + repr(self.warnings), + repr(self.statistics), + self.is_error, + )[ + :1024 + ] + + @classmethod + def _from_generated(cls, result): + return cls( + id=result.id, + classifications=[ + ClassificationCategory._from_generated(e) # pylint: disable=protected-access + for e in result.class_property + ], + warnings=[ + TextAnalyticsWarning._from_generated( # pylint: disable=protected-access + w + ) + for w in result.warnings + ], + statistics=TextDocumentStatistics._from_generated( # pylint: disable=protected-access + result.statistics + ), + ) diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_response_handlers.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_response_handlers.py index 83d729188993..0e320712a7b5 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_response_handlers.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_response_handlers.py @@ -36,6 +36,7 @@ RecognizeCustomEntitiesResult, ClassifyDocumentResult, ActionPointerKind, + DynamicClassificationResult, ) @@ -279,6 +280,15 @@ def classify_document_result( ) +@prepare_result +def dynamic_classification_result( + categories, results, *args, **kwargs +): # pylint: disable=unused-argument + return DynamicClassificationResult._from_generated( # pylint: disable=protected-access + categories + ) + + def healthcare_extract_page_data( doc_id_order, obj, health_job_state ): # pylint: disable=unused-argument diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py index 9089ce06d2c0..4a36889ab010 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py @@ -34,7 +34,8 @@ pii_entities_result, healthcare_paged_result, analyze_paged_result, - _get_result_from_continuation_token + _get_result_from_continuation_token, + dynamic_classification_result, ) from ._lro import ( @@ -65,6 +66,7 @@ ClassifyDocumentResult, AnalyzeHealthcareEntitiesAction, _AnalyzeActionsType, + DynamicClassificationResult, ) from ._check import is_language_api, string_index_type_compatibility @@ -1627,3 +1629,100 @@ def begin_multi_label_classify( except HttpResponseError as error: return process_http_response_error(error) + + @distributed_trace + @validate_multiapi_args( + version_method_added="2022-10-01-preview", + ) + def dynamically_classify( + self, + documents: Union[List[str], List[TextDocumentInput], List[Dict[str, str]]], + categories: List[str], + **kwargs: Any, + ) -> List[Union[DynamicClassificationResult, DocumentError]]: + """Perform dynamic classification on a batch of documents. + + On the fly classification of the input documents into one or multiple categories. + Assigns either one or multiple categories per document. This type of classification + doesn't require model training. + + See https://aka.ms/azsdk/textanalytics/data-limits for service data limits. + + :param documents: The set of documents to process as part of this batch. + If you wish to specify the ID and language on a per-item basis you must + use as input a list[:class:`~azure.ai.textanalytics.TextDocumentInput`] or a list + of dict representations of :class:`~azure.ai.textanalytics.TextDocumentInput`, + like `{"id": "1", "language": "en", "text": "hello world"}`. + :type documents: + list[str] or list[~azure.ai.textanalytics.TextDocumentInput] or list[dict[str, str]] + :param list[str] categories: A list of categories to which input is classified to. + :keyword classification_type: Specifies either one or multiple categories per document. Defaults + to multi classification which may return more than one class for each document. Known values + are: "Single" and "Multi". + :paramtype classification_type: str or ~azure.ai.textanalytics.ClassificationType + :keyword str language: The 2 letter ISO 639-1 representation of language for the + entire batch. For example, use "en" for English; "es" for Spanish etc. + If not set, uses "en" for English as default. Per-document language will + take precedence over whole batch language. See https://aka.ms/talangs for + supported languages in Language API. + :keyword str model_version: This value indicates which model will + be used for scoring, e.g. "latest", "2019-10-01". If a model-version + is not specified, the API will default to the latest, non-preview version. + See here for more info: https://aka.ms/text-analytics-model-versioning + :keyword bool show_stats: If set to true, response will contain document + level statistics in the `statistics` field of the document-level response. + :keyword bool disable_service_logs: If set to true, you opt-out of having your text input + logged on the service side for troubleshooting. By default, the Language service logs your + input text for 48 hours, solely to allow for troubleshooting issues in providing you with + the service's natural language processing functions. Setting this parameter to true, + disables input logging and may limit our ability to remediate issues that occur. Please see + Cognitive Services Compliance and Privacy notes at https://aka.ms/cs-compliance for + additional details, and Microsoft Responsible AI principles at + https://www.microsoft.com/ai/responsible-ai. + :return: The combined list of :class:`~azure.ai.textanalytics.DynamicClassificationResult` and + :class:`~azure.ai.textanalytics.DocumentError` in the order the original documents + were passed in. + :rtype: list[~azure.ai.textanalytics.DynamicClassificationResult or ~azure.ai.textanalytics.DocumentError] + :raises ~azure.core.exceptions.HttpResponseError: + + .. versionadded:: 2022-10-01-preview + The *dynamically_classify* client method. + + .. admonition:: Example: + + .. literalinclude:: ../samples/sample_dynamically_classify.py + :start-after: [START dynamic_classification] + :end-before: [END dynamic_classification] + :language: python + :dedent: 4 + :caption: Perform dynamic classification on a batch of documents. + """ + language_arg = kwargs.pop("language", None) + language = language_arg if language_arg is not None else self._default_language + docs = _validate_input(documents, "language", language) + model_version = kwargs.pop("model_version", None) + show_stats = kwargs.pop("show_stats", None) + disable_service_logs = kwargs.pop("disable_service_logs", None) + classification_type = kwargs.pop("classification_type", None) + + try: + models = self._client.models(api_version=self._api_version) + return cast( + List[Union[DynamicClassificationResult, DocumentError]], + self._client.analyze_text( + body=models.AnalyzeTextDynamicClassificationInput( + analysis_input={"documents": docs}, + parameters=models.DynamicClassificationTaskParameters( + categories=categories, + logging_opt_out=disable_service_logs, + model_version=model_version, + classification_type=classification_type, + ) + ), + show_stats=show_stats, + cls=kwargs.pop("cls", dynamic_classification_result), + **kwargs + ) + ) + except HttpResponseError as error: + return process_http_response_error(error) diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py index a32b917497e3..ecdc0fe5a513 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py @@ -25,7 +25,8 @@ sentiment_result, language_result, pii_entities_result, - _get_result_from_continuation_token + _get_result_from_continuation_token, + dynamic_classification_result, ) from ._response_handlers_async import healthcare_paged_result, analyze_paged_result from .._models import ( @@ -50,7 +51,8 @@ SingleLabelClassifyAction, MultiLabelClassifyAction, ClassifyDocumentResult, - AnalyzeHealthcareEntitiesAction + AnalyzeHealthcareEntitiesAction, + DynamicClassificationResult, ) from .._check import is_language_api, string_index_type_compatibility from .._lro import TextAnalyticsOperationResourcePolling @@ -1624,3 +1626,100 @@ async def begin_multi_label_classify( except HttpResponseError as error: return process_http_response_error(error) + + @distributed_trace_async + @validate_multiapi_args( + version_method_added="2022-10-01-preview", + ) + async def dynamically_classify( + self, + documents: Union[List[str], List[TextDocumentInput], List[Dict[str, str]]], + categories: List[str], + **kwargs: Any, + ) -> List[Union[DynamicClassificationResult, DocumentError]]: + """Perform dynamic classification on a batch of documents. + + On the fly classification of the input documents into one or multiple categories. + Assigns either one or multiple categories per document. This type of classification + doesn't require model training. + + See https://aka.ms/azsdk/textanalytics/data-limits for service data limits. + + :param documents: The set of documents to process as part of this batch. + If you wish to specify the ID and language on a per-item basis you must + use as input a list[:class:`~azure.ai.textanalytics.TextDocumentInput`] or a list + of dict representations of :class:`~azure.ai.textanalytics.TextDocumentInput`, + like `{"id": "1", "language": "en", "text": "hello world"}`. + :type documents: + list[str] or list[~azure.ai.textanalytics.TextDocumentInput] or list[dict[str, str]] + :param list[str] categories: A list of categories to which input is classified to. + :keyword classification_type: Specifies either one or multiple categories per document. Defaults + to multi classification which may return more than one class for each document. Known values + are: "Single" and "Multi". + :paramtype classification_type: str or ~azure.ai.textanalytics.ClassificationType + :keyword str language: The 2 letter ISO 639-1 representation of language for the + entire batch. For example, use "en" for English; "es" for Spanish etc. + If not set, uses "en" for English as default. Per-document language will + take precedence over whole batch language. See https://aka.ms/talangs for + supported languages in Language API. + :keyword str model_version: This value indicates which model will + be used for scoring, e.g. "latest", "2019-10-01". If a model-version + is not specified, the API will default to the latest, non-preview version. + See here for more info: https://aka.ms/text-analytics-model-versioning + :keyword bool show_stats: If set to true, response will contain document + level statistics in the `statistics` field of the document-level response. + :keyword bool disable_service_logs: If set to true, you opt-out of having your text input + logged on the service side for troubleshooting. By default, the Language service logs your + input text for 48 hours, solely to allow for troubleshooting issues in providing you with + the service's natural language processing functions. Setting this parameter to true, + disables input logging and may limit our ability to remediate issues that occur. Please see + Cognitive Services Compliance and Privacy notes at https://aka.ms/cs-compliance for + additional details, and Microsoft Responsible AI principles at + https://www.microsoft.com/ai/responsible-ai. + :return: The combined list of :class:`~azure.ai.textanalytics.DynamicClassificationResult` and + :class:`~azure.ai.textanalytics.DocumentError` in the order the original documents + were passed in. + :rtype: list[~azure.ai.textanalytics.DynamicClassificationResult or ~azure.ai.textanalytics.DocumentError] + :raises ~azure.core.exceptions.HttpResponseError: + + .. versionadded:: 2022-10-01-preview + The *dynamically_classify* client method. + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_dynamically_classify_async.py + :start-after: [START dynamic_classification_async] + :end-before: [END dynamic_classification_async] + :language: python + :dedent: 4 + :caption: Perform dynamic classification on a batch of documents. + """ + language_arg = kwargs.pop("language", None) + language = language_arg if language_arg is not None else self._default_language + docs = _validate_input(documents, "language", language) + model_version = kwargs.pop("model_version", None) + show_stats = kwargs.pop("show_stats", None) + disable_service_logs = kwargs.pop("disable_service_logs", None) + classification_type = kwargs.pop("classification_type", None) + + try: + models = self._client.models(api_version=self._api_version) + return cast( + List[Union[DynamicClassificationResult, DocumentError]], + await self._client.analyze_text( + body=models.AnalyzeTextDynamicClassificationInput( + analysis_input={"documents": docs}, + parameters=models.DynamicClassificationTaskParameters( + categories=categories, + logging_opt_out=disable_service_logs, + model_version=model_version, + classification_type=classification_type, + ) + ), + show_stats=show_stats, + cls=kwargs.pop("cls", dynamic_classification_result), + **kwargs + ) + ) + except HttpResponseError as error: + return process_http_response_error(error) diff --git a/sdk/textanalytics/azure-ai-textanalytics/samples/README.md b/sdk/textanalytics/azure-ai-textanalytics/samples/README.md index 3b2fdf7ea289..8afc6eea3e77 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/samples/README.md +++ b/sdk/textanalytics/azure-ai-textanalytics/samples/README.md @@ -34,6 +34,7 @@ These sample programs show common scenarios for the Text Analytics client's offe |[sample_multi_label_classify.py][multi_label_classify_sample] and [sample_multi_label_classify_async.py][multi_label_classify_sample_async]|Use a custom model to classify documents into multiple categories| |[sample_model_version.py][sample_model_version] and [sample_model_version_async.py][sample_model_version_async]|Set the model version for pre-built Text Analytics models| |[sample_analyze_healthcare_action.py][sample_analyze_healthcare_action] and [sample_analyze_healthcare_action_async.py][sample_analyze_healthcare_action_async]|Run a healthcare and PII analysis together| +|[sample_dynamically_classify.py][dynamically_classify_sample] and [sample_dynamically_classify_async.py][dynamically_classify_sample_async]|Dynamically classify documents without needing to train a model.| ## Prerequisites * Python 3.7 or later is required to use this package @@ -112,6 +113,8 @@ what you can do with the Azure Text Analytics client library. [sample_model_version_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_model_version_async.py [sample_analyze_healthcare_action]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_healthcare_action.py [sample_analyze_healthcare_action_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_healthcare_action_async.py +[dynamically_classify_sample]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamically_classify.py +[dynamically_classify_sample_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamically_classify_async.py [pip]: https://pypi.org/project/pip/ [azure_subscription]: https://azure.microsoft.com/free/ [azure_language_account]: https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account?tabs=singleservice%2Cwindows From 5396643077ddca9f6b42f5ec08520655a9d2d2f3 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Thu, 22 Sep 2022 15:34:33 -0700 Subject: [PATCH 2/5] add samples + fix init import --- .../azure/ai/textanalytics/__init__.py | 2 +- .../sample_dynamically_classify_async.py | 71 +++++++++++++++++++ .../samples/sample_dynamically_classify.py | 64 +++++++++++++++++ 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamically_classify_async.py create mode 100644 sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamically_classify.py diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py index 2090c2f8ada9..93bb6f03192d 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py @@ -59,7 +59,7 @@ TextAnalysisKind, DynamicClassificationResult ) -from ._generated import ClassificationType +from ._generated.models import ClassificationType from ._lro import AnalyzeHealthcareEntitiesLROPoller, AnalyzeActionsLROPoller, TextAnalysisLROPoller __all__ = [ diff --git a/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamically_classify_async.py b/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamically_classify_async.py new file mode 100644 index 000000000000..948b0e0530a3 --- /dev/null +++ b/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamically_classify_async.py @@ -0,0 +1,71 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_dynamically_classify_async.py + +DESCRIPTION: + This sample demonstrates how to dynamically classify documents into one or multiple categories. + No model training is required to use dynamic classification. + +USAGE: + python sample_dynamically_classify_async.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_LANGUAGE_ENDPOINT - the endpoint to your Language resource. + 2) AZURE_LANGUAGE_KEY - your Language subscription key +""" + + +from cgitb import text +import os +import asyncio + + +async def sample_dynamically_classify_async() -> None: + # [START dynamic_classification_async] + from azure.core.credentials import AzureKeyCredential + from azure.ai.textanalytics.aio import TextAnalyticsClient + + endpoint = os.environ["AZURE_LANGUAGE_ENDPOINT"] + key = os.environ["AZURE_LANGUAGE_KEY"] + + text_analytics_client = TextAnalyticsClient( + endpoint=endpoint, + credential=AzureKeyCredential(key), + ) + documents = [ + "The WHO is issuing a warning about Monkey Pox.", + "Mo Salah plays in Liverpool FC in England.", + ] + + async with text_analytics_client: + results = await text_analytics_client.dynamically_classify( + documents, + categories=["Health", "Politics", "Music", "Sports"], + classification_type="Multi" + ) + document_results = [] + async for result in results: + document_results.append(result) + + for doc, classification_result in zip(documents, document_results): + if classification_result.kind == "DynamicClassification": + classifications = classification_result.classifications + print(f"\n'{doc}' classifications:\n") + for classification in classifications: + print("Category '{}' with confidence score {}.".format( + classification.category, classification.confidence_score + )) + elif classification_result.is_error is True: + print("Document '{}' has an error with code '{}' and message '{}'".format( + doc, classification_result.code, classification_result.message + )) + # [END dynamic_classification_async] + + +if __name__ == "__main__": + asyncio.run(sample_dynamically_classify_async()) diff --git a/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamically_classify.py b/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamically_classify.py new file mode 100644 index 000000000000..8e86ec006838 --- /dev/null +++ b/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamically_classify.py @@ -0,0 +1,64 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_dynamically_classify.py + +DESCRIPTION: + This sample demonstrates how to dynamically classify documents into one or multiple categories. + No model training is required to use dynamic classification. + +USAGE: + python sample_dynamically_classify.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_LANGUAGE_ENDPOINT - the endpoint to your Language resource. + 2) AZURE_LANGUAGE_KEY - your Language subscription key +""" + + +import os + + +def sample_dynamically_classify() -> None: + # [START dynamic_classification] + from azure.core.credentials import AzureKeyCredential + from azure.ai.textanalytics import TextAnalyticsClient + + endpoint = os.environ["AZURE_LANGUAGE_ENDPOINT"] + key = os.environ["AZURE_LANGUAGE_KEY"] + + text_analytics_client = TextAnalyticsClient( + endpoint=endpoint, + credential=AzureKeyCredential(key), + ) + documents = [ + "The WHO is issuing a warning about Monkey Pox.", + "Mo Salah plays in Liverpool FC in England.", + ] + result = text_analytics_client.dynamically_classify( + documents, + categories=["Health", "Politics", "Music", "Sports"], + classification_type="Multi" + ) + + for doc, classification_result in zip(documents, result): + if classification_result.kind == "DynamicClassification": + classifications = classification_result.classifications + print(f"\n'{doc}' classifications:\n") + for classification in classifications: + print("Category '{}' with confidence score {}.".format( + classification.category, classification.confidence_score + )) + elif classification_result.is_error is True: + print("Document '{}' has an error with code '{}' and message '{}'".format( + doc, classification_result.code, classification_result.message + )) + # [END dynamic_classification] + + +if __name__ == "__main__": + sample_dynamically_classify() From fc4eaede6c3c6d49bdb1bdaf016525c06076fb36 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Mon, 26 Sep 2022 15:15:44 -0700 Subject: [PATCH 3/5] rename dynamically_classify -> dynamic_classification --- sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md | 2 +- sdk/textanalytics/azure-ai-textanalytics/README.md | 8 ++++---- .../azure/ai/textanalytics/_text_analytics_client.py | 6 +++--- .../textanalytics/aio/_text_analytics_client_async.py | 6 +++--- .../azure-ai-textanalytics/samples/README.md | 6 +++--- ...async.py => sample_dynamic_classification_async.py} | 10 +++++----- ...ly_classify.py => sample_dynamic_classification.py} | 10 +++++----- 7 files changed, 24 insertions(+), 24 deletions(-) rename sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/{sample_dynamically_classify_async.py => sample_dynamic_classification_async.py} (89%) rename sdk/textanalytics/azure-ai-textanalytics/samples/{sample_dynamically_classify.py => sample_dynamic_classification.py} (90%) diff --git a/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md b/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md index 968e74e0deef..45b29901d4d7 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md +++ b/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md @@ -3,7 +3,7 @@ ## 5.3.0b1 (Unreleased) ### Features Added -- Added `dynamically_classify` client method to dynamically classify documents without needing to train a model. +- Added the `dynamic_classification` client method to perform dynamic classification on documents without needing to train a model. ### Breaking Changes diff --git a/sdk/textanalytics/azure-ai-textanalytics/README.md b/sdk/textanalytics/azure-ai-textanalytics/README.md index cc9f654f6dbc..6f0f75988acb 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/README.md +++ b/sdk/textanalytics/azure-ai-textanalytics/README.md @@ -255,7 +255,7 @@ The following section provides several code snippets covering some of the most c - [Custom Entity Recognition][recognize_custom_entities_sample] - [Custom Single Label Classification][single_label_classify_sample] - [Custom Multi Label Classification][multi_label_classify_sample] -- [Dynamic Classification][dynamically_classify_sample] +- [Dynamic Classification][dynamic_classification_sample] ### Analyze sentiment @@ -666,7 +666,7 @@ Common scenarios - Custom Entity Recognition: [sample_recognize_custom_entities.py][recognize_custom_entities_sample] ([async_version][recognize_custom_entities_sample_async]) - Custom Single Label Classification: [sample_single_label_classify.py][single_label_classify_sample] ([async_version][single_label_classify_sample_async]) - Custom Multi Label Classification: [sample_multi_label_classify.py][multi_label_classify_sample] ([async_version][multi_label_classify_sample_async]) -- Dynamic Classification: [sample_dynamically_classify.py][dynamically_classify_sample] ([async_version][dynamically_classify_sample_async]) +- Dynamic Classification: [sample_dynamic_classification.py][dynamic_classification_sample] ([async_version][dynamic_classification_sample_async]) Advanced scenarios @@ -769,8 +769,8 @@ This project has adopted the [Microsoft Open Source Code of Conduct][code_of_con [multi_label_classify_sample]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_multi_label_classify.py [multi_label_classify_sample_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_multi_label_classify_async.py [healthcare_action_sample]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_healthcare_action.py -[dynamically_classify_sample]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamically_classify.py -[dynamically_classify_sample_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamically_classify_async.py +[dynamic_classification_sample]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamic_classification.py +[dynamic_classification_sample_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamic_classification_async.py [cla]: https://cla.microsoft.com [code_of_conduct]: https://opensource.microsoft.com/codeofconduct/ [coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py index 4a36889ab010..1b5cad0f9711 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py @@ -1634,7 +1634,7 @@ def begin_multi_label_classify( @validate_multiapi_args( version_method_added="2022-10-01-preview", ) - def dynamically_classify( + def dynamic_classification( self, documents: Union[List[str], List[TextDocumentInput], List[Dict[str, str]]], categories: List[str], @@ -1686,11 +1686,11 @@ def dynamically_classify( :raises ~azure.core.exceptions.HttpResponseError: .. versionadded:: 2022-10-01-preview - The *dynamically_classify* client method. + The *dynamic_classification* client method. .. admonition:: Example: - .. literalinclude:: ../samples/sample_dynamically_classify.py + .. literalinclude:: ../samples/sample_dynamic_classification.py :start-after: [START dynamic_classification] :end-before: [END dynamic_classification] :language: python diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py index ecdc0fe5a513..73a5e14dfff8 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py @@ -1631,7 +1631,7 @@ async def begin_multi_label_classify( @validate_multiapi_args( version_method_added="2022-10-01-preview", ) - async def dynamically_classify( + async def dynamic_classification( self, documents: Union[List[str], List[TextDocumentInput], List[Dict[str, str]]], categories: List[str], @@ -1683,11 +1683,11 @@ async def dynamically_classify( :raises ~azure.core.exceptions.HttpResponseError: .. versionadded:: 2022-10-01-preview - The *dynamically_classify* client method. + The *dynamic_classification* client method. .. admonition:: Example: - .. literalinclude:: ../samples/async_samples/sample_dynamically_classify_async.py + .. literalinclude:: ../samples/async_samples/sample_dynamic_classification_async.py :start-after: [START dynamic_classification_async] :end-before: [END dynamic_classification_async] :language: python diff --git a/sdk/textanalytics/azure-ai-textanalytics/samples/README.md b/sdk/textanalytics/azure-ai-textanalytics/samples/README.md index 8afc6eea3e77..26cc576310ce 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/samples/README.md +++ b/sdk/textanalytics/azure-ai-textanalytics/samples/README.md @@ -34,7 +34,7 @@ These sample programs show common scenarios for the Text Analytics client's offe |[sample_multi_label_classify.py][multi_label_classify_sample] and [sample_multi_label_classify_async.py][multi_label_classify_sample_async]|Use a custom model to classify documents into multiple categories| |[sample_model_version.py][sample_model_version] and [sample_model_version_async.py][sample_model_version_async]|Set the model version for pre-built Text Analytics models| |[sample_analyze_healthcare_action.py][sample_analyze_healthcare_action] and [sample_analyze_healthcare_action_async.py][sample_analyze_healthcare_action_async]|Run a healthcare and PII analysis together| -|[sample_dynamically_classify.py][dynamically_classify_sample] and [sample_dynamically_classify_async.py][dynamically_classify_sample_async]|Dynamically classify documents without needing to train a model.| +|[sample_dynamic_classification.py][dynamic_classification_sample] and [sample_dynamic_classification_async.py][dynamic_classification_sample_async]|Dynamically classify documents without needing to train a model.| ## Prerequisites * Python 3.7 or later is required to use this package @@ -113,8 +113,8 @@ what you can do with the Azure Text Analytics client library. [sample_model_version_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_model_version_async.py [sample_analyze_healthcare_action]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_healthcare_action.py [sample_analyze_healthcare_action_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_healthcare_action_async.py -[dynamically_classify_sample]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamically_classify.py -[dynamically_classify_sample_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamically_classify_async.py +[dynamic_classification_sample]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamic_classification.py +[dynamic_classification_sample_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamic_classification_async.py [pip]: https://pypi.org/project/pip/ [azure_subscription]: https://azure.microsoft.com/free/ [azure_language_account]: https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account?tabs=singleservice%2Cwindows diff --git a/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamically_classify_async.py b/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamic_classification_async.py similarity index 89% rename from sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamically_classify_async.py rename to sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamic_classification_async.py index 948b0e0530a3..588cc3cf778d 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamically_classify_async.py +++ b/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_dynamic_classification_async.py @@ -5,14 +5,14 @@ # -------------------------------------------------------------------------- """ -FILE: sample_dynamically_classify_async.py +FILE: sample_dynamic_classification_async.py DESCRIPTION: This sample demonstrates how to dynamically classify documents into one or multiple categories. No model training is required to use dynamic classification. USAGE: - python sample_dynamically_classify_async.py + python sample_dynamic_classification_async.py Set the environment variables with your own values before running the sample: 1) AZURE_LANGUAGE_ENDPOINT - the endpoint to your Language resource. @@ -25,7 +25,7 @@ import asyncio -async def sample_dynamically_classify_async() -> None: +async def sample_dynamic_classification_async() -> None: # [START dynamic_classification_async] from azure.core.credentials import AzureKeyCredential from azure.ai.textanalytics.aio import TextAnalyticsClient @@ -43,7 +43,7 @@ async def sample_dynamically_classify_async() -> None: ] async with text_analytics_client: - results = await text_analytics_client.dynamically_classify( + results = await text_analytics_client.dynamic_classification( documents, categories=["Health", "Politics", "Music", "Sports"], classification_type="Multi" @@ -68,4 +68,4 @@ async def sample_dynamically_classify_async() -> None: if __name__ == "__main__": - asyncio.run(sample_dynamically_classify_async()) + asyncio.run(sample_dynamic_classification_async()) diff --git a/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamically_classify.py b/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamic_classification.py similarity index 90% rename from sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamically_classify.py rename to sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamic_classification.py index 8e86ec006838..f36e57f91cd8 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamically_classify.py +++ b/sdk/textanalytics/azure-ai-textanalytics/samples/sample_dynamic_classification.py @@ -5,14 +5,14 @@ # -------------------------------------------------------------------------- """ -FILE: sample_dynamically_classify.py +FILE: sample_dynamic_classification.py DESCRIPTION: This sample demonstrates how to dynamically classify documents into one or multiple categories. No model training is required to use dynamic classification. USAGE: - python sample_dynamically_classify.py + python sample_dynamic_classification.py Set the environment variables with your own values before running the sample: 1) AZURE_LANGUAGE_ENDPOINT - the endpoint to your Language resource. @@ -23,7 +23,7 @@ import os -def sample_dynamically_classify() -> None: +def sample_dynamic_classification() -> None: # [START dynamic_classification] from azure.core.credentials import AzureKeyCredential from azure.ai.textanalytics import TextAnalyticsClient @@ -39,7 +39,7 @@ def sample_dynamically_classify() -> None: "The WHO is issuing a warning about Monkey Pox.", "Mo Salah plays in Liverpool FC in England.", ] - result = text_analytics_client.dynamically_classify( + result = text_analytics_client.dynamic_classification( documents, categories=["Health", "Politics", "Music", "Sports"], classification_type="Multi" @@ -61,4 +61,4 @@ def sample_dynamically_classify() -> None: if __name__ == "__main__": - sample_dynamically_classify() + sample_dynamic_classification() From dc2d1f2fdc5990ff61e79226203c4e3bf031b3de Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Fri, 21 Oct 2022 09:30:52 -0700 Subject: [PATCH 4/5] test wip --- .../tests/test_dynamic_classification.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification.py diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification.py b/sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification.py new file mode 100644 index 000000000000..c70c21ab9edc --- /dev/null +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification.py @@ -0,0 +1,35 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest +import platform +import functools +from testcase import TextAnalyticsTest, TextAnalyticsPreparer +from testcase import TextAnalyticsClientPreparer as _TextAnalyticsClientPreparer +from devtools_testutils import recorded_by_proxy +from azure.ai.textanalytics import ( + DynamicClassificationResult, + TextAnalyticsClient, + ClassificationType, +) + +# pre-apply the client_cls positional argument so it needn't be explicitly passed below +TextAnalyticsClientPreparer = functools.partial(_TextAnalyticsClientPreparer, TextAnalyticsClient) + + +class TestDynamicClassification(TextAnalyticsTest): + + @TextAnalyticsPreparer() + @TextAnalyticsClientPreparer() + @recorded_by_proxy + def test_dynamic_classification(self, client): + documents = [ + "The WHO is issuing a warning about Monkey Pox.", + "Mo Salah plays in Liverpool FC in England.", + ] + result = client.dynamic_classification( + documents, + categories=["Health", "Politics", "Music", "Sports"] + ) From fe7adc6f2d548ef2bd7116540f3ec39d506cb558 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Mon, 31 Oct 2022 11:29:55 -0700 Subject: [PATCH 5/5] add patch for swagger/service mismatch on classifications property and record tests --- .../azure/ai/textanalytics/_models.py | 12 +- ...sificationtest_dynamic_classification.json | 122 ++++++++++++++++++ ...iontest_dynamic_classification_single.json | 85 ++++++++++++ ...sificationtest_dynamic_classification.json | 121 +++++++++++++++++ ...iontest_dynamic_classification_single.json | 84 ++++++++++++ .../tests/test_dynamic_classification.py | 28 +++- .../test_dynamic_classification_async.py | 63 +++++++++ 7 files changed, 512 insertions(+), 3 deletions(-) create mode 100644 sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification.pyTestDynamicClassificationtest_dynamic_classification.json create mode 100644 sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification.pyTestDynamicClassificationtest_dynamic_classification_single.json create mode 100644 sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification_async.pyTestDynamicClassificationtest_dynamic_classification.json create mode 100644 sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification_async.pyTestDynamicClassificationtest_dynamic_classification_single.json create mode 100644 sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification_async.py diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py index edc95f823482..ef56955b9e00 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py @@ -2484,6 +2484,12 @@ def __repr__(self): @classmethod def _from_generated(cls, result): + # FIXME: https://github.com/Azure/azure-sdk-for-python/issues/27089 + if isinstance(result, dict): + return cls( + category=result["category"], + confidence_score=result["confidenceScore"] + ) return cls( category=result.category, confidence_score=result.confidence_score @@ -2806,11 +2812,13 @@ def __repr__(self): @classmethod def _from_generated(cls, result): + # FIXME: https://github.com/Azure/azure-sdk-for-python/issues/27089 + classes = result.class_property or result.additional_properties.get("classifications", None) return cls( id=result.id, classifications=[ - ClassificationCategory._from_generated(e) # pylint: disable=protected-access - for e in result.class_property + ClassificationCategory._from_generated(c) # pylint: disable=protected-access + for c in classes ], warnings=[ TextAnalyticsWarning._from_generated( # pylint: disable=protected-access diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification.pyTestDynamicClassificationtest_dynamic_classification.json b/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification.pyTestDynamicClassificationtest_dynamic_classification.json new file mode 100644 index 000000000000..27c3ad3c7a65 --- /dev/null +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification.pyTestDynamicClassificationtest_dynamic_classification.json @@ -0,0 +1,122 @@ +{ + "Entries": [ + { + "RequestUri": "https://fakeendpoint.cognitiveservices.azure.com/language/:analyze-text?api-version=2022-10-01-preview\u0026showStats=true", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Content-Length": "314", + "Content-Type": "application/json", + "User-Agent": "azsdk-python-ai-textanalytics/5.3.0b1 Python/3.10.0 (Windows-10-10.0.22621-SP0)" + }, + "RequestBody": { + "kind": "DynamicClassification", + "analysisInput": { + "documents": [ + { + "id": "0", + "text": "The WHO is issuing a warning about Monkey Pox.", + "language": "en" + }, + { + "id": "1", + "text": "Mo Salah plays in Liverpool FC in England.", + "language": "en" + } + ] + }, + "parameters": { + "categories": [ + "Health", + "Politics", + "Music", + "Sports" + ] + } + }, + "StatusCode": 200, + "ResponseHeaders": { + "apim-request-id": "5450fe87-ff4d-4b32-bb0d-8c507151f230", + "Content-Length": "786", + "Content-Type": "application/json; charset=utf-8", + "csp-billing-usage": "CognitiveServices.TextAnalytics.BatchScoring=0,CognitiveServices.TextAnalytics.TextRecords=2", + "Date": "Mon, 31 Oct 2022 18:15:35 GMT", + "Set-Cookie": ".AspNetCore.Mvc.CookieTempDataProvider=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; samesite=lax; httponly", + "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", + "X-Content-Type-Options": "nosniff", + "x-envoy-upstream-service-time": "259", + "x-ms-region": "UK South" + }, + "ResponseBody": { + "kind": "DynamicClassificationResults", + "results": { + "statistics": { + "documentsCount": 2, + "validDocumentsCount": 2, + "erroneousDocumentsCount": 0, + "transactionsCount": 0 + }, + "documents": [ + { + "id": "0", + "classifications": [ + { + "category": "Health", + "confidenceScore": 0.88 + }, + { + "category": "Music", + "confidenceScore": 0.04 + }, + { + "category": "Sports", + "confidenceScore": 0.04 + }, + { + "category": "Politics", + "confidenceScore": 0.03 + } + ], + "statistics": { + "charactersCount": 46, + "transactionsCount": 1 + }, + "warnings": [] + }, + { + "id": "1", + "classifications": [ + { + "category": "Sports", + "confidenceScore": 0.99 + }, + { + "category": "Music", + "confidenceScore": 0.0 + }, + { + "category": "Health", + "confidenceScore": 0.0 + }, + { + "category": "Politics", + "confidenceScore": 0.0 + } + ], + "statistics": { + "charactersCount": 42, + "transactionsCount": 1 + }, + "warnings": [] + } + ], + "errors": [], + "modelVersion": "2022-10-01-preview" + } + } + } + ], + "Variables": {} +} diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification.pyTestDynamicClassificationtest_dynamic_classification_single.json b/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification.pyTestDynamicClassificationtest_dynamic_classification_single.json new file mode 100644 index 000000000000..3cbb3840d142 --- /dev/null +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification.pyTestDynamicClassificationtest_dynamic_classification_single.json @@ -0,0 +1,85 @@ +{ + "Entries": [ + { + "RequestUri": "https://fakeendpoint.cognitiveservices.azure.com/language/:analyze-text?api-version=2022-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Connection": "keep-alive", + "Content-Length": "346", + "Content-Type": "application/json", + "User-Agent": "azsdk-python-ai-textanalytics/5.3.0b1 Python/3.10.0 (Windows-10-10.0.22621-SP0)" + }, + "RequestBody": { + "kind": "DynamicClassification", + "analysisInput": { + "documents": [ + { + "id": "0", + "text": "The WHO is issuing a warning about Monkey Pox.", + "language": "en" + }, + { + "id": "1", + "text": "Mo Salah plays in Liverpool FC in England.", + "language": "en" + } + ] + }, + "parameters": { + "classificationType": "single", + "categories": [ + "Health", + "Politics", + "Music", + "Sports" + ] + } + }, + "StatusCode": 200, + "ResponseHeaders": { + "apim-request-id": "39b6e5e4-706a-496d-89a5-f270058d0b85", + "Content-Length": "293", + "Content-Type": "application/json; charset=utf-8", + "csp-billing-usage": "CognitiveServices.TextAnalytics.BatchScoring=0,CognitiveServices.TextAnalytics.TextRecords=2", + "Date": "Mon, 31 Oct 2022 18:21:09 GMT", + "Set-Cookie": ".AspNetCore.Mvc.CookieTempDataProvider=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; samesite=lax; httponly", + "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", + "X-Content-Type-Options": "nosniff", + "x-envoy-upstream-service-time": "460", + "x-ms-region": "UK South" + }, + "ResponseBody": { + "kind": "DynamicClassificationResults", + "results": { + "documents": [ + { + "id": "0", + "classifications": [ + { + "category": "Health", + "confidenceScore": 0.88 + } + ], + "warnings": [] + }, + { + "id": "1", + "classifications": [ + { + "category": "Sports", + "confidenceScore": 0.99 + } + ], + "warnings": [] + } + ], + "errors": [], + "modelVersion": "2022-10-01-preview" + } + } + } + ], + "Variables": {} +} diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification_async.pyTestDynamicClassificationtest_dynamic_classification.json b/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification_async.pyTestDynamicClassificationtest_dynamic_classification.json new file mode 100644 index 000000000000..0d40127b7310 --- /dev/null +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification_async.pyTestDynamicClassificationtest_dynamic_classification.json @@ -0,0 +1,121 @@ +{ + "Entries": [ + { + "RequestUri": "https://fakeendpoint.cognitiveservices.azure.com/language/:analyze-text?api-version=2022-10-01-preview\u0026showStats=true", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "314", + "Content-Type": "application/json", + "User-Agent": "azsdk-python-ai-textanalytics/5.3.0b1 Python/3.10.0 (Windows-10-10.0.22621-SP0)" + }, + "RequestBody": { + "kind": "DynamicClassification", + "analysisInput": { + "documents": [ + { + "id": "0", + "text": "The WHO is issuing a warning about Monkey Pox.", + "language": "en" + }, + { + "id": "1", + "text": "Mo Salah plays in Liverpool FC in England.", + "language": "en" + } + ] + }, + "parameters": { + "categories": [ + "Health", + "Politics", + "Music", + "Sports" + ] + } + }, + "StatusCode": 200, + "ResponseHeaders": { + "apim-request-id": "54a767ee-0785-4f2b-9ada-13944ce33b46", + "Content-Length": "786", + "Content-Type": "application/json; charset=utf-8", + "csp-billing-usage": "CognitiveServices.TextAnalytics.BatchScoring=0,CognitiveServices.TextAnalytics.TextRecords=2", + "Date": "Mon, 31 Oct 2022 18:26:15 GMT", + "Set-Cookie": ".AspNetCore.Mvc.CookieTempDataProvider=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; samesite=lax; httponly", + "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", + "X-Content-Type-Options": "nosniff", + "x-envoy-upstream-service-time": "391", + "x-ms-region": "UK South" + }, + "ResponseBody": { + "kind": "DynamicClassificationResults", + "results": { + "statistics": { + "documentsCount": 2, + "validDocumentsCount": 2, + "erroneousDocumentsCount": 0, + "transactionsCount": 0 + }, + "documents": [ + { + "id": "0", + "classifications": [ + { + "category": "Health", + "confidenceScore": 0.88 + }, + { + "category": "Music", + "confidenceScore": 0.04 + }, + { + "category": "Sports", + "confidenceScore": 0.04 + }, + { + "category": "Politics", + "confidenceScore": 0.03 + } + ], + "statistics": { + "charactersCount": 46, + "transactionsCount": 1 + }, + "warnings": [] + }, + { + "id": "1", + "classifications": [ + { + "category": "Sports", + "confidenceScore": 0.99 + }, + { + "category": "Music", + "confidenceScore": 0.0 + }, + { + "category": "Health", + "confidenceScore": 0.0 + }, + { + "category": "Politics", + "confidenceScore": 0.0 + } + ], + "statistics": { + "charactersCount": 42, + "transactionsCount": 1 + }, + "warnings": [] + } + ], + "errors": [], + "modelVersion": "2022-10-01-preview" + } + } + } + ], + "Variables": {} +} diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification_async.pyTestDynamicClassificationtest_dynamic_classification_single.json b/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification_async.pyTestDynamicClassificationtest_dynamic_classification_single.json new file mode 100644 index 000000000000..06026b2bbf75 --- /dev/null +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/recordings/test_dynamic_classification_async.pyTestDynamicClassificationtest_dynamic_classification_single.json @@ -0,0 +1,84 @@ +{ + "Entries": [ + { + "RequestUri": "https://fakeendpoint.cognitiveservices.azure.com/language/:analyze-text?api-version=2022-10-01-preview", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "346", + "Content-Type": "application/json", + "User-Agent": "azsdk-python-ai-textanalytics/5.3.0b1 Python/3.10.0 (Windows-10-10.0.22621-SP0)" + }, + "RequestBody": { + "kind": "DynamicClassification", + "analysisInput": { + "documents": [ + { + "id": "0", + "text": "The WHO is issuing a warning about Monkey Pox.", + "language": "en" + }, + { + "id": "1", + "text": "Mo Salah plays in Liverpool FC in England.", + "language": "en" + } + ] + }, + "parameters": { + "classificationType": "single", + "categories": [ + "Health", + "Politics", + "Music", + "Sports" + ] + } + }, + "StatusCode": 200, + "ResponseHeaders": { + "apim-request-id": "55451f57-4098-4dfd-8c31-17de26b798ea", + "Content-Length": "293", + "Content-Type": "application/json; charset=utf-8", + "csp-billing-usage": "CognitiveServices.TextAnalytics.BatchScoring=0,CognitiveServices.TextAnalytics.TextRecords=2", + "Date": "Mon, 31 Oct 2022 18:26:15 GMT", + "Set-Cookie": ".AspNetCore.Mvc.CookieTempDataProvider=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; samesite=lax; httponly", + "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", + "X-Content-Type-Options": "nosniff", + "x-envoy-upstream-service-time": "366", + "x-ms-region": "UK South" + }, + "ResponseBody": { + "kind": "DynamicClassificationResults", + "results": { + "documents": [ + { + "id": "0", + "classifications": [ + { + "category": "Health", + "confidenceScore": 0.88 + } + ], + "warnings": [] + }, + { + "id": "1", + "classifications": [ + { + "category": "Sports", + "confidenceScore": 0.99 + } + ], + "warnings": [] + } + ], + "errors": [], + "modelVersion": "2022-10-01-preview" + } + } + } + ], + "Variables": {} +} diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification.py b/sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification.py index c70c21ab9edc..03bd7e22c595 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification.py +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification.py @@ -31,5 +31,31 @@ def test_dynamic_classification(self, client): ] result = client.dynamic_classification( documents, - categories=["Health", "Politics", "Music", "Sports"] + categories=["Health", "Politics", "Music", "Sports"], + show_stats=True ) + for res in result: + assert res.id + assert res.statistics + for classification in res.classifications: + assert classification.category + assert classification.confidence_score is not None + + @TextAnalyticsPreparer() + @TextAnalyticsClientPreparer() + @recorded_by_proxy + def test_dynamic_classification_single(self, client): + documents = [ + "The WHO is issuing a warning about Monkey Pox.", + "Mo Salah plays in Liverpool FC in England.", + ] + result = client.dynamic_classification( + documents, + categories=["Health", "Politics", "Music", "Sports"], + classification_type="single" + ) + for res in result: + assert res.id + assert len(res.classifications) == 1 + assert res.classifications[0].category + assert res.classifications[0].confidence_score is not None diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification_async.py b/sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification_async.py new file mode 100644 index 000000000000..7a8f020c76fc --- /dev/null +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/test_dynamic_classification_async.py @@ -0,0 +1,63 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest +import platform +import functools +from testcase import TextAnalyticsTest, TextAnalyticsPreparer +from testcase import TextAnalyticsClientPreparer as _TextAnalyticsClientPreparer +from devtools_testutils.aio import recorded_by_proxy_async +from azure.ai.textanalytics.aio import TextAnalyticsClient +from azure.ai.textanalytics import ( + DynamicClassificationResult, + ClassificationType, +) + +# pre-apply the client_cls positional argument so it needn't be explicitly passed below +TextAnalyticsClientPreparer = functools.partial(_TextAnalyticsClientPreparer, TextAnalyticsClient) + + +class TestDynamicClassification(TextAnalyticsTest): + + @TextAnalyticsPreparer() + @TextAnalyticsClientPreparer() + @recorded_by_proxy_async + async def test_dynamic_classification(self, client): + documents = [ + "The WHO is issuing a warning about Monkey Pox.", + "Mo Salah plays in Liverpool FC in England.", + ] + async with client: + result = await client.dynamic_classification( + documents, + categories=["Health", "Politics", "Music", "Sports"], + show_stats=True + ) + for res in result: + assert res.id + assert res.statistics + for classification in res.classifications: + assert classification.category + assert classification.confidence_score is not None + + @TextAnalyticsPreparer() + @TextAnalyticsClientPreparer() + @recorded_by_proxy_async + async def test_dynamic_classification_single(self, client): + documents = [ + "The WHO is issuing a warning about Monkey Pox.", + "Mo Salah plays in Liverpool FC in England.", + ] + async with client: + result = await client.dynamic_classification( + documents, + categories=["Health", "Politics", "Music", "Sports"], + classification_type="single" + ) + for res in result: + assert res.id + assert len(res.classifications) == 1 + assert res.classifications[0].category + assert res.classifications[0].confidence_score is not None