diff --git a/.vscode/cspell.json b/.vscode/cspell.json index f3507259eada..eaeed23e1e47 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -912,7 +912,7 @@ }, { "filename": "sdk/cognitivelanguage/azure-ai-language-questionanswering/**", - "words": [ "qnas", "qnamaker", "ADTO" ], + "words": [ "qnas", "qnamaker", "ADTO", "tfidf", "ngram" ], "caseSensitive": false }, { diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/CHANGELOG.md b/sdk/cognitivelanguage/azure-ai-language-questionanswering/CHANGELOG.md index 80c46bcd4c5f..b637da9fc8e3 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/CHANGELOG.md +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/CHANGELOG.md @@ -1,15 +1,28 @@ # Release History -## 1.1.1 (Unreleased) - -### Features Added +## 2.0.0b1 (2025-10-27) ### Breaking Changes -### Bugs Fixed +* Authoring functionality (project creation, knowledge source management, deployment operations) has been removed from this package and moved to a separate dedicated authoring package / namespace. All references to `AuthoringClient`, and related authoring operations have been eliminated from the runtime client distribution. +* Dropped support for Python versions earlier than 3.9 (aligns with Azure SDK Python support policy going forward). +* Model base class change: all public model types now inherit from `MutableMapping[str, Any]` (dict-like) instead of the previous `Model` base class. As a result they now support standard mutable mapping behavior (key iteration, item assignment, etc.) and any code depending on methods/properties inherited from the old base class should be reviewed/updated. +* Removed client constructor keyword argument `default_language`. Per-call language control is now done explicitly via the `language` property on `AnswersFromTextOptions` (and related options models). The service default remains `"en"` if a language is not supplied. To change the effective language: + * Pass `language=""` when constructing `AnswersFromTextOptions` (e.g. `"es"`, `"zh-Hans"`). + * Or create / select a project in the desired language in [Language Studio](https://language.azure.com) when authoring knowledge bases. + + The previous implicit fallback (client-level `default_language`) has been removed to avoid hidden state and to encourage explicit specification or project-level configuration. +* Removed support for passing metadata as `Dict[str,str]` to `MetadataFilter`. Tuple form `[("key","value"), ...]` and `List[MetadataRecord]` remain supported. + +### Features Added + +* Documentation improvements: expanded README with authentication guidance, AAD usage, async examples, and troubleshooting section. ### Other Changes +* Internal refactoring and dependency grooming in preparation for the authoring/runtime split. +* Changed samples and README examples to use explicit `AnswersOptions` / `AnswersFromTextOptions` objects as the first argument when calling `get_answers` and `get_answers_from_text`. + ## 1.1.0 (2022-10-13) ### Breaking Changes @@ -102,4 +115,4 @@ ## 1.0.0b1 (2021-07-27) ### Features Added -* Initial release - supports querying from text records and knowledge bases. +* Initial release - supports querying from text records and knowledge bases. \ No newline at end of file diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/MANIFEST.in b/sdk/cognitivelanguage/azure-ai-language-questionanswering/MANIFEST.in index 224ded3a8f0b..be2f27d850d8 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/MANIFEST.in +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/MANIFEST.in @@ -1,8 +1,8 @@ include *.md include LICENSE +include azure/ai/language/questionanswering/py.typed +recursive-include tests *.py +recursive-include samples *.py *.md include azure/__init__.py include azure/ai/__init__.py include azure/ai/language/__init__.py -recursive-include tests *.py -recursive-include samples *.py *.md -include azure/ai/language/questionanswering/py.typed diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/README.md b/sdk/cognitivelanguage/azure-ai-language-questionanswering/README.md index 8c01e7da22bb..6df3f7d260f9 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/README.md +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/README.md @@ -1,110 +1,105 @@ -# Azure Cognitive Language Service Question Answering client library for Python +# Azure AI Language Question Answering client library for Python -Question Answering is a cloud-based API service that lets you create a conversational question-and-answer layer over your existing data. Use it to build a knowledge base by extracting questions and answers from your semi-structured content, including FAQ, manuals, and documents. Answer users’ questions with the best answers from the QnAs in your knowledge base—automatically. Your knowledge base gets smarter, too, as it continually learns from users' behavior. +Question Answering is an Azure AI Language capability that lets you build a conversational, question‑and‑answer layer over your existing data. It extracts question/answer pairs from semi‑structured content (FAQ pages, manuals, documents) and uses them to answer user questions with the most relevant answer automatically. [Source code][questionanswering_client_src] | [Package (PyPI)][questionanswering_pypi_package] | [Package (Conda)](https://anaconda.org/microsoft/azure-ai-language-questionanswering/) -| [API reference documentation][questionanswering_refdocs] +| [API reference][questionanswering_refdocs] | [Product documentation][questionanswering_docs] | [Samples][questionanswering_samples] -| [Analysis REST API documentation][questionanswering_rest_docs] -| [Authoring REST API documentation][questionanswering_rest_docs_authoring] +| [Question Answering REST API][questionanswering_rest_docs] -## _Disclaimer_ - -_Azure SDK Python packages support for Python 2.7 ended 01 January 2022. For more information and questions, please refer to https://github.com/Azure/azure-sdk-for-python/issues/20691_ +> _Python 2.7 is not supported. For details see the Azure SDK for Python end-of-support notice._ ## Getting started ### Prerequisites -- Python 3.7 or later is required to use this package. -- An [Azure subscription][azure_subscription] -- A [Language Service][language_service] resource +* Python 3.9 or later. +* An [Azure subscription][azure_subscription]. +* An Azure [Language resource][language_service] (with a custom domain endpoint if you plan to use Azure Active Directory authentication). ### Install the package Install the Azure Question Answering client library for Python with [pip][pip_link]: ```bash -pip install azure-ai-language-questionanswering +python -m pip install azure-ai-language-questionanswering ``` -> Note: this version of the client library defaults to the service API version `2021-10-01`. +> This version of the client library targets the service REST API version `2025-05-15-preview`. ### Authenticate the client -In order to interact with the Question Answering service, you'll need to create an instance of the [QuestionAnsweringClient][questionanswering_client_class] class or an instance of the [AuthoringClient][authoring_client_class] for managing projects within your resource. You will need an **endpoint**, and an **API key** to instantiate a client object. For more information regarding authenticating with Cognitive Services, see [Authenticate requests to Azure Cognitive Services][cognitive_auth]. +In order to interact with the Question Answering service, you'll create an instance of the [QuestionAnsweringClient][questionanswering_client_class] (or the [AuthoringClient][authoring_client_class] in the separate authoring package). The **recommended** approach is to use Azure Active Directory via `DefaultAzureCredential` from the [azure-identity][azure_identity_credentials] library. This avoids embedding keys, enables managed identity in production, and unifies authentication across Azure SDKs. -#### Get an API key +> Important: To use Azure AD (AAD) you must use your resource's **custom subdomain** endpoint (for example: `https://.cognitiveservices.azure.com/`); legacy regional generic endpoints (e.g., `https://eastus.api.cognitive.microsoft.com`) do **not** support AAD token authentication. -You can get the **endpoint** and an **API key** from the Language resource in the [Azure Portal][azure_portal]. +#### Recommended: DefaultAzureCredential -Alternatively, use the [Azure CLI][azure_cli] command shown below to get the API key from the Language resource. +Prerequisites for AAD authentication: -```powershell -az cognitiveservices account keys list --resource-group --name -``` +* [Install azure-identity][install_azure_identity] +* [Register an AAD application][register_aad_app] +* [Grant access][grant_role_access] to the Language resource (e.g., assign the "Cognitive Services Language Reader" role, plus writer roles if needed for authoring) -#### Create QuestionAnsweringClient +Set these environment variables only if you’re using a service principal with a client secret (otherwise, if you rely on Azure CLI / VS Code login or Managed Identity, you can skip this step): +AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET -Once you've determined your **endpoint** and **API key** you can instantiate a [QuestionAnsweringClient][questionanswering_client_class]: +Then create the client: ```python -from azure.core.credentials import AzureKeyCredential +from azure.identity import DefaultAzureCredential from azure.ai.language.questionanswering import QuestionAnsweringClient -endpoint = "https://{myaccount}.api.cognitive.microsoft.com" -credential = AzureKeyCredential("{api-key}") +endpoint = "https://.cognitiveservices.azure.com/" # custom subdomain endpoint +credential = DefaultAzureCredential() client = QuestionAnsweringClient(endpoint, credential) ``` -#### Create AuthoringClient -With your endpoint and API key, you can instantiate a [AuthoringClient][authoring_client_class]: +Authoring (if using the separate authoring package): ```python -from azure.core.credentials import AzureKeyCredential +from azure.identity import DefaultAzureCredential from azure.ai.language.questionanswering.authoring import AuthoringClient -endpoint = "https://{myaccount}.api.cognitive.microsoft.com" -credential = AzureKeyCredential("{api-key}") +endpoint = "https://.cognitiveservices.azure.com/" +credential = DefaultAzureCredential() -client = AuthoringClient(endpoint, credential) +authoring_client = AuthoringClient(endpoint, credential) ``` -#### Create a client with an Azure Active Directory Credential - -To use an [Azure Active Directory (AAD) token credential][cognitive_authentication_aad], -provide an instance of the desired credential type obtained from the -[azure-identity][azure_identity_credentials] library. -Note that regional endpoints do not support AAD authentication. Create a [custom subdomain][custom_subdomain] -name for your resource in order to use this type of authentication. - -Authentication with AAD requires some initial setup: - -- [Install azure-identity][install_azure_identity] -- [Register a new AAD application][register_aad_app] -- [Grant access][grant_role_access] to the Language service by assigning the "Cognitive Services Language Reader" role to your service principal. +#### Alternative: API key credential -After setup, you can choose which type of [credential][azure_identity_credentials] from azure.identity to use. -As an example, [DefaultAzureCredential][default_azure_credential] -can be used to authenticate the client: +For quick starts or scripts where you have not yet configured AAD, you can use an API key with `AzureKeyCredential`. You can obtain the key from the Azure Portal, or via the CLI: -Set the values of the client ID, tenant ID, and client secret of the AAD application as environment variables: -`AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_CLIENT_SECRET` +```powershell +az cognitiveservices account keys list --resource-group --name +``` -Use the returned token credential to authenticate the client: +Then: ```python +from azure.core.credentials import AzureKeyCredential from azure.ai.language.questionanswering import QuestionAnsweringClient -from azure.identity import DefaultAzureCredential -credential = DefaultAzureCredential() -client = QuestionAnsweringClient(endpoint="https://.cognitiveservices.azure.com/", credential=credential) +endpoint = "https://.cognitiveservices.azure.com" # regional or custom subdomain +credential = AzureKeyCredential("") + +client = QuestionAnsweringClient(endpoint, credential) ``` +> Note: You can seamlessly switch between key and AAD auth — no code changes beyond the credential object. + +**Why DefaultAzureCredential?** + +* Eliminates hard‑coded secrets +* Works locally (developer tools), in CI (service principal / federated), and in production (Managed Identity) +* Centralizes token acquisition & caching +* Supports future auth enhancements without code changes + ## Key concepts ### QuestionAnsweringClient @@ -112,205 +107,183 @@ client = QuestionAnsweringClient(endpoint="https://.cogniti The [QuestionAnsweringClient][questionanswering_client_class] is the primary interface for asking questions using a knowledge base with your own information, or text input using pre-trained models. For asynchronous operations, an async `QuestionAnsweringClient` is in the `azure.ai.language.questionanswering.aio` namespace. -### AuthoringClient -The [AuthoringClient][authoring_client_class] provides an interface for managing Question Answering projects. Examples of the available operations include creating and deploying projects, updating your knowledge sources, and updating question and answer pairs. It provides both synchronous and asynchronous APIs. +> Authoring (project creation, knowledge source management, deployment) has moved to a separate package and is intentionally not covered in this runtime client README. ## Examples -### QuestionAnsweringClient +### QuestionAnsweringClient usage examples + The `azure-ai-language-questionanswering` client library provides both synchronous and asynchronous APIs. -- [Ask a question](#ask-a-question "Ask a question") -- [Ask a follow-up question](#ask-a-follow-up-question "Ask a follow-up question") -- [Create a new project](#create-a-new-project "Create a new project") -- [Add a knowledge source](#add-a-knowledge-source "Add a knowledge source") -- [Deploy your project](#deploy-your-project "Deploy your project") -- [Asynchronous operations](#asynchronous-operations "Asynchronous operations") +* [Ask a question](#ask-a-question-options-object) +* [Ask a follow-up question](#follow-up-question-options-object) +* [Asynchronous operations](#async-usage-options-object) -#### Ask a question +#### Ask a question (options object) The only input required to ask a question using a knowledge base is just the question itself: ```python import os -from azure.core.credentials import AzureKeyCredential +from azure.identity import DefaultAzureCredential from azure.ai.language.questionanswering import QuestionAnsweringClient +from azure.ai.language.questionanswering.models import AnswersOptions -endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] -key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - -client = QuestionAnsweringClient(endpoint, AzureKeyCredential(key)) +endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] # must be a custom subdomain for AAD +client = QuestionAnsweringClient(endpoint, DefaultAzureCredential()) -output = client.get_answers( +options = AnswersOptions( question="How long should my Surface battery last?", - project_name="FAQ", - deployment_name="production" + # Optional extra parameters: + # confidence_threshold=0.2, + # top=5, + # short_answer_options=qna.ShortAnswerOptions(top=1) ) -for candidate in output.answers: - print("({}) {}".format(candidate.confidence, candidate.answer)) - print("Source: {}".format(candidate.source)) +response = client.get_answers(options, project_name="FAQ", deployment_name="production") +for answer in response.answers: + print(f"({answer.confidence:.2f}) {answer.answer}") + print(f"Source: {answer.source}") ``` -You can set additional keyword options to limit the number of answers, specify a minimum confidence score, and more. +You can also pass optional parameters like `confidence_threshold`, `top`, or `short_answer_options` inside the `AnswersOptions` object. -#### Ask a follow-up question +#### Ask a question (flattened) -If your knowledge base is configured for [chit-chat][questionanswering_docs_chat], the answers from the knowledge base may include suggested [prompts for follow-up questions][questionanswering_refdocs_prompts] to initiate a conversation. You can ask a follow-up question by providing the ID of your chosen answer as the context for the continued conversation: +For convenience, you can also call `get_answers` directly with keyword parameters: ```python -import os -from azure.core.credentials import AzureKeyCredential -from azure.ai.language.questionanswering import QuestionAnsweringClient -from azure.ai.language.questionanswering import models - -endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] -key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - -client = QuestionAnsweringClient(endpoint, AzureKeyCredential(key)) - -output = client.get_answers( - question="How long should charging take?", - answer_context=models.KnowledgeBaseAnswerContext( - previous_qna_id=previous_answer.qna_id - ), +# Equivalent flattened form - same result as above +response = client.get_answers( + question="How long should my Surface battery last?", project_name="FAQ", - deployment_name="production" + deployment_name="production", + # Optional parameters can be passed directly: + # confidence_threshold=0.2, + # top=5 ) -for candidate in output.answers: - print("({}) {}".format(candidate.confidence, candidate.answer)) - print("Source: {}".format(candidate.source)) + +for answer in response.answers: + print(f"({answer.confidence:.2f}) {answer.answer}") + print(f"Source: {answer.source}") ``` -#### Create a new project +#### Follow-up question (options object) + +If your knowledge base is configured for [chit-chat][questionanswering_docs_chat], the answers from the knowledge base may include suggested [prompts for follow-up questions][questionanswering_refdocs_prompts] to initiate a conversation. You can ask a follow-up question by providing the ID of your chosen answer as the context for the continued conversation: ```python -import os -from azure.core.credentials import AzureKeyCredential -from azure.ai.language.questionanswering.authoring import AuthoringClient +from azure.ai.language.questionanswering.models import AnswersOptions, KnowledgeBaseAnswerContext -# get service secrets -endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] -key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - -# create client -client = AuthoringClient(endpoint, AzureKeyCredential(key)) -with client: - - # create project - project_name = "IssacNewton" - project = client.create_project( - project_name=project_name, - options={ - "description": "biography of Sir Issac Newton", - "language": "en", - "multilingualResource": True, - "settings": { - "defaultAnswer": "no answer" - } - }) - - print("view created project info:") - print("\tname: {}".format(project["projectName"])) - print("\tlanguage: {}".format(project["language"])) - print("\tdescription: {}".format(project["description"])) +follow_up_options = AnswersOptions( + question="How long should charging take?", + answer_context=KnowledgeBaseAnswerContext(previous_qna_id=previous_answer.qna_id), +) +follow_up = client.get_answers(follow_up_options, project_name="FAQ", deployment_name="production") + +for answer in follow_up.answers: + print(f"({answer.confidence:.2f}) {answer.answer}") ``` -#### Add a knowledge source +#### Follow-up question (flattened) ```python import os -from azure.core.credentials import AzureKeyCredential -from azure.ai.language.questionanswering.authoring import AuthoringClient +from azure.identity import DefaultAzureCredential +from azure.ai.language.questionanswering import QuestionAnsweringClient +from azure.ai.language.questionanswering.models import KnowledgeBaseAnswerContext -# get service secrets endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] -key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - -# create client -client = AuthoringClient(endpoint, AzureKeyCredential(key)) - -project_name = "IssacNewton" -update_sources_poller = client.begin_update_sources( - project_name=project_name, - sources=[ - { - "op": "add", - "value": { - "displayName": "Issac Newton Bio", - "sourceUri": "https://wikipedia.org/wiki/Isaac_Newton", - "sourceKind": "url" - } - } - ] -) -update_sources_poller.result() +client = QuestionAnsweringClient(endpoint, DefaultAzureCredential()) -# list sources -print("list project sources") -sources = client.list_sources( - project_name=project_name +output = client.get_answers( + question="How long should charging take?", + answer_context=KnowledgeBaseAnswerContext(previous_qna_id=previous_answer.qna_id), + project_name="FAQ", + deployment_name="production" ) -for source in sources: - print("project: {}".format(source["displayName"])) - print("\tsource: {}".format(source["source"])) - print("\tsource Uri: {}".format(source["sourceUri"])) - print("\tsource kind: {}".format(source["sourceKind"])) +for candidate in output.answers: + print(f"({candidate.confidence}) {candidate.answer}") + print(f"Source: {candidate.source}") ``` -#### Deploy your project +#### Async usage (options object) +The above examples can also be run asynchronously using the clients in the `aio` namespace: ```python import os -from azure.core.credentials import AzureKeyCredential -from azure.ai.language.questionanswering.authoring import AuthoringClient +import asyncio +from azure.identity import DefaultAzureCredential +from azure.ai.language.questionanswering.aio import QuestionAnsweringClient +from azure.ai.language.questionanswering.models import AnswersOptions -# get service secrets -endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] -key = os.environ["AZURE_QUESTIONANSWERING_KEY"] +async def main(): + endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] + client = QuestionAnsweringClient(endpoint, DefaultAzureCredential()) + options = AnswersOptions(question="How long should my Surface battery last?") + response = await client.get_answers(options, project_name="FAQ", deployment_name="production") + for answer in response.answers: + print(f"({answer.confidence:.2f}) {answer.answer}") -# create client -client = AuthoringClient(endpoint, AzureKeyCredential(key)) +asyncio.run(main()) +``` -project_name = "IssacNewton" +#### Async usage (flattened) -# deploy project -deployment_poller = client.begin_deploy_project( - project_name=project_name, - deployment_name="production" -) -deployment_poller.result() +```python +import os +import asyncio +from azure.identity import DefaultAzureCredential +from azure.ai.language.questionanswering.aio import QuestionAnsweringClient -# list all deployments -deployments = client.list_deployments( - project_name=project_name -) +async def main(): + endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] + client = QuestionAnsweringClient(endpoint, DefaultAzureCredential()) + output = await client.get_answers( + question="How long should my Surface battery last?", + project_name="FAQ", + deployment_name="production" + ) + for candidate in output.answers: + print(f"({candidate.confidence:.2f}) {candidate.answer}") -print("view project deployments") -for d in deployments: - print(d) +asyncio.run(main()) ``` -#### Asynchronous operations +#### Filtering with metadata (QueryFilters) -The above examples can also be run asynchronously using the clients in the `aio` namespace: +You can narrow answers using metadata stored in your knowledge base: ```python -import os -from azure.core.credentials import AzureKeyCredential -from azure.ai.language.questionanswering.aio import QuestionAnsweringClient +from azure.ai.language.questionanswering.models import ( + AnswersOptions, + QueryFilters, + MetadataFilter, + MetadataRecord +) -endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] -key = os.environ["AZURE_QUESTIONANSWERING_KEY"] +# Tuple form (supported) +metadata_filter_tuple = MetadataFilter(metadata=[("product", "surface"), ("locale", "en-US")]) -client = QuestionAnsweringClient(endpoint, AzureKeyCredential(key)) +# MetadataRecord form (recommended for static typing) +metadata_filter_records = MetadataFilter(metadata=[ + MetadataRecord(key="product", value="surface"), + MetadataRecord(key="locale", value="en-US") +]) -output = await client.get_answers( +options = AnswersOptions( question="How long should my Surface battery last?", - project_name="FAQ", - deployment_name="production" + filters=QueryFilters(metadata_filter=metadata_filter_tuple), + confidence_threshold=0.2, + top=3 ) + +resp = client.get_answers(options, project_name="FAQ", deployment_name="production") +for ans in resp.answers: + print(f"{ans.answer} ({ans.confidence:.2f})") + +# Note: Passing metadata as a dict (e.g. {'product': 'surface'}) is no longer supported. ``` ## Optional Configuration @@ -349,13 +322,22 @@ level. Detailed DEBUG level logging, including request/response bodies and unredacted headers, can be enabled on a client with the `logging_enable` argument. -See full SDK logging documentation with examples [here][sdk_logging_docs]. +See the full SDK logging documentation with examples in the [logging guidance][sdk_logging_docs]. + +## API Usage Notes + +This library supports both explicit options objects (like `AnswersOptions` for `get_answers` and `AnswersFromTextOptions` for `get_answers_from_text`) and flattened keyword parameters for convenience. Both approaches are fully supported and equivalent (and work regardless of whether you use `DefaultAzureCredential` or an API key): + +* **Options object approach**: `client.get_answers(AnswersOptions(question="...", top=5), project_name="...", deployment_name="...")` +* **Flattened parameters**: `client.get_answers(question="...", top=5, project_name="...", deployment_name="...")` + +Choose whichever style best fits your coding preferences - both produce identical results. ## Next steps -- View our [samples][questionanswering_samples]. -- Read about the different [features][questionanswering_docs_features] of the Question Answering service. -- Try our service [demos][questionanswering_docs_demos]. +* View our [samples][questionanswering_samples]. +* Read about the different [features][questionanswering_docs_features] of the Question Answering service. +* Try our service [demos][questionanswering_docs_demos]. ## Contributing @@ -388,19 +370,15 @@ This project has adopted the [Microsoft Open Source Code of Conduct][code_of_con [authoring_client_class]: https://aka.ms/azsdk/python/questionansweringauthoringclient [questionanswering_refdocs_prompts]: https://azuresdkdocs.z19.web.core.windows.net/python/azure-ai-language-questionanswering/latest/azure.ai.language.questionanswering.models.html#azure.ai.language.questionanswering.models.KnowledgeBaseAnswerDialog [questionanswering_client_src]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/ -[questionanswering_docs]: https://azure.microsoft.com/services/cognitive-services/qna-maker/ +[questionanswering_docs]: https://learn.microsoft.com/azure/ai-services/language-service/question-answering/overview [questionanswering_docs_chat]: https://learn.microsoft.com/azure/cognitive-services/qnamaker/how-to/chit-chat-knowledge-base -[questionanswering_docs_demos]: https://azure.microsoft.com/services/cognitive-services/qna-maker/#demo -[questionanswering_docs_features]: https://azure.microsoft.com/services/cognitive-services/qna-maker/#features +[questionanswering_docs_features]: https://learn.microsoft.com/azure/ai-services/language-service/question-answering/overview#key-capabilities [questionanswering_pypi_package]: https://pypi.org/project/azure-ai-language-questionanswering/ [questionanswering_refdocs]: https://azuresdkdocs.z19.web.core.windows.net/python/azure-ai-language-questionanswering/latest/azure.ai.language.questionanswering.html [questionanswering_samples]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/README.md -[cognitive_authentication_aad]: https://learn.microsoft.com/azure/cognitive-services/authentication#authenticate-with-azure-active-directory [azure_identity_credentials]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#credentials -[custom_subdomain]: https://learn.microsoft.com/azure/cognitive-services/authentication#create-a-resource-with-a-custom-subdomain [install_azure_identity]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#install-the-package [register_aad_app]: https://learn.microsoft.com/azure/cognitive-services/authentication#assign-a-role-to-a-service-principal [grant_role_access]: https://learn.microsoft.com/azure/cognitive-services/authentication#assign-a-role-to-a-service-principal -[default_azure_credential]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#defaultazurecredential -[questionanswering_rest_docs]: https://learn.microsoft.com/rest/api/language/question-answering/question-answering?view=rest-language-question-answering-2023-04-01 -[questionanswering_rest_docs_authoring]: https://learn.microsoft.com/rest/api/language/question-answering-authoring/question-answering-projects?view=rest-language-question-answering-authoring-2023-04-01 +[questionanswering_rest_docs]: https://learn.microsoft.com/rest/api/language/question-answering/operation-groups?view=rest-language-question-answering-2025-05-15-preview +[questionanswering_docs_demos]: https://learn.microsoft.com/azure/ai-services/language-service/question-answering/overview#try-it diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/_metadata.json b/sdk/cognitivelanguage/azure-ai-language-questionanswering/_metadata.json new file mode 100644 index 000000000000..253921f335be --- /dev/null +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/_metadata.json @@ -0,0 +1,3 @@ +{ + "apiVersion": "2025-05-15-preview" +} \ No newline at end of file diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/apiview-properties.json b/sdk/cognitivelanguage/azure-ai-language-questionanswering/apiview-properties.json new file mode 100644 index 000000000000..bd20d06212d5 --- /dev/null +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/apiview-properties.json @@ -0,0 +1,38 @@ +{ + "CrossLanguagePackageId": "Language.QuestionAnswering", + "CrossLanguageDefinitionId": { + "azure.ai.language.questionanswering.models.AnswersFromTextOptions": "Language.QuestionAnswering.AnswersFromTextOptions", + "azure.ai.language.questionanswering.models.AnswersFromTextResult": "Language.QuestionAnswering.AnswersFromTextResult", + "azure.ai.language.questionanswering.models.AnswersOptions": "Language.QuestionAnswering.AnswersOptions", + "azure.ai.language.questionanswering.models.AnswerSpan": "Language.QuestionAnswering.AnswerSpan", + "azure.ai.language.questionanswering.models.AnswersResult": "Language.QuestionAnswering.AnswersResult", + "azure.ai.language.questionanswering.models.Error": "Language.QuestionAnswering.Error", + "azure.ai.language.questionanswering.models.ErrorResponse": "Language.QuestionAnswering.ErrorResponse", + "azure.ai.language.questionanswering.models.InnerErrorModel": "Language.QuestionAnswering.InnerErrorModel", + "azure.ai.language.questionanswering.models.KnowledgeBaseAnswer": "Language.QuestionAnswering.KnowledgeBaseAnswer", + "azure.ai.language.questionanswering.models.KnowledgeBaseAnswerContext": "Language.QuestionAnswering.KnowledgeBaseAnswerContext", + "azure.ai.language.questionanswering.models.KnowledgeBaseAnswerDialog": "Language.QuestionAnswering.KnowledgeBaseAnswerDialog", + "azure.ai.language.questionanswering.models.KnowledgeBaseAnswerPrompt": "Language.QuestionAnswering.KnowledgeBaseAnswerPrompt", + "azure.ai.language.questionanswering.models.MatchingPolicy": "Language.QuestionAnswering.MatchingPolicy", + "azure.ai.language.questionanswering.models.MetadataFilter": "Language.QuestionAnswering.MetadataFilter", + "azure.ai.language.questionanswering.models.MetadataRecord": "Language.QuestionAnswering.MetadataRecord", + "azure.ai.language.questionanswering.models.PrebuiltQueryMatchingPolicy": "Language.QuestionAnswering.PrebuiltQueryMatchingPolicy", + "azure.ai.language.questionanswering.models.QueryFilters": "Language.QuestionAnswering.QueryFilters", + "azure.ai.language.questionanswering.models.QueryPreferences": "Language.QuestionAnswering.QueryPreferences", + "azure.ai.language.questionanswering.models.ShortAnswerOptions": "Language.QuestionAnswering.ShortAnswerOptions", + "azure.ai.language.questionanswering.models.TextAnswer": "Language.QuestionAnswering.TextAnswer", + "azure.ai.language.questionanswering.models.TextDocument": "Language.QuestionAnswering.TextDocument", + "azure.ai.language.questionanswering.models.ErrorCode": "Language.QuestionAnswering.ErrorCode", + "azure.ai.language.questionanswering.models.InnerErrorCode": "Language.QuestionAnswering.InnerErrorCode", + "azure.ai.language.questionanswering.models.RankerKind": "Language.QuestionAnswering.RankerKind", + "azure.ai.language.questionanswering.models.LogicalOperationKind": "Language.QuestionAnswering.LogicalOperationKind", + "azure.ai.language.questionanswering.models.Scorer": "Language.QuestionAnswering.Scorer", + "azure.ai.language.questionanswering.models.MatchingPolicyKind": "Language.QuestionAnswering.MatchingPolicyKind", + "azure.ai.language.questionanswering.models.MatchingPolicyFieldsType": "Language.QuestionAnswering.MatchingPolicyFieldsType", + "azure.ai.language.questionanswering.models.StringIndexType": "Language.QuestionAnswering.StringIndexType", + "azure.ai.language.questionanswering.QuestionAnsweringClient.get_answers": "Language.QuestionAnsweringClientCustomizations.QuestionAnsweringClientOperations.getAnswers", + "azure.ai.language.questionanswering.aio.QuestionAnsweringClient.get_answers": "Language.QuestionAnsweringClientCustomizations.QuestionAnsweringClientOperations.getAnswers", + "azure.ai.language.questionanswering.QuestionAnsweringClient.get_answers_from_text": "Language.QuestionAnsweringClientCustomizations.QuestionAnsweringClientOperations.getAnswersFromText", + "azure.ai.language.questionanswering.aio.QuestionAnsweringClient.get_answers_from_text": "Language.QuestionAnsweringClientCustomizations.QuestionAnsweringClientOperations.getAnswersFromText" + } +} \ No newline at end of file diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/assets.json b/sdk/cognitivelanguage/azure-ai-language-questionanswering/assets.json index 017b86b554e5..17f266b9de4c 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/assets.json +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "python", "TagPrefix": "python/cognitivelanguage/azure-ai-language-questionanswering", - "Tag": "python/cognitivelanguage/azure-ai-language-questionanswering_1f7e05ddea" + "Tag": "python/cognitivelanguage/azure-ai-language-questionanswering_4cf6afbd99" } diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/__init__.py index 8db66d3d0f0f..d55ccad1f573 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/__init__.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/__init__.py @@ -1 +1 @@ -__path__ = __import__("pkgutil").extend_path(__path__, __name__) +__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/__init__.py index 8db66d3d0f0f..d55ccad1f573 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/__init__.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/__init__.py @@ -1 +1 @@ -__path__ = __import__("pkgutil").extend_path(__path__, __name__) +__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/__init__.py index 8db66d3d0f0f..d55ccad1f573 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/__init__.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/__init__.py @@ -1 +1 @@ -__path__ = __import__("pkgutil").extend_path(__path__, __name__) +__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/__init__.py index 7f17dbede9b9..86394629a6ec 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/__init__.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/__init__.py @@ -2,21 +2,31 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position -from ._patch import QuestionAnsweringClient +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + +from ._client import QuestionAnsweringClient # type: ignore from ._version import VERSION __version__ = VERSION - +try: + from ._patch import __all__ as _patch_all + from ._patch import * +except ImportError: + _patch_all = [] from ._patch import patch_sdk as _patch_sdk __all__ = [ "QuestionAnsweringClient", ] - +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore _patch_sdk() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_client.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_client.py index ac731b198e85..14dd0690f011 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_client.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_client.py @@ -2,46 +2,47 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- from copy import deepcopy -from typing import Any +from typing import Any, TYPE_CHECKING, Union +from typing_extensions import Self from azure.core import PipelineClient from azure.core.credentials import AzureKeyCredential from azure.core.pipeline import policies from azure.core.rest import HttpRequest, HttpResponse -from . import models as _models from ._configuration import QuestionAnsweringClientConfiguration -from ._operations import QuestionAnsweringClientOperationsMixin -from ._serialization import Deserializer, Serializer +from ._operations import _QuestionAnsweringClientOperationsMixin +from ._utils.serialization import Deserializer, Serializer +if TYPE_CHECKING: + from azure.core.credentials import TokenCredential -class QuestionAnsweringClient( - QuestionAnsweringClientOperationsMixin -): # pylint: disable=client-accepts-api-version-keyword - """The language service API is a suite of natural language processing (NLP) skills built with - best-in-class Microsoft machine learning algorithms. The API can be used to analyze - unstructured text for tasks such as sentiment analysis, key phrase extraction, language - detection and question answering. Further documentation can be found in - https://learn.microsoft.com/azure/cognitive-services/text-analytics/overview. + +class QuestionAnsweringClient(_QuestionAnsweringClientOperationsMixin): + """QuestionAnsweringClient. :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.api.cognitiveservices.azure.com). Required. + https://.api.cognitiveservices.azure.com). Required. :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. Required. - :type credential: ~azure.core.credentials.AzureKeyCredential - :keyword api_version: Api Version. Default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. + :param credential: Credential used to authenticate requests to the service. Is either a key + credential type or a token credential type. Required. + :type credential: ~azure.core.credentials.AzureKeyCredential or + ~azure.core.credentials.TokenCredential + :keyword api_version: The API version to use for this operation. Default value is + "2025-05-15-preview". Note that overriding this default value may result in unsupported + behavior. :paramtype api_version: str """ - def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) -> None: + def __init__(self, endpoint: str, credential: Union[AzureKeyCredential, "TokenCredential"], **kwargs: Any) -> None: _endpoint = "{Endpoint}/language" self._config = QuestionAnsweringClientConfiguration(endpoint=endpoint, credential=credential, **kwargs) + _policies = kwargs.pop("policies", None) if _policies is None: _policies = [ @@ -61,9 +62,8 @@ def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) ] self._client: PipelineClient = PipelineClient(base_url=_endpoint, policies=_policies, **kwargs) - client_models = {k: v for k, v in _models.__dict__.items() if isinstance(v, type)} - self._serialize = Serializer(client_models) - self._deserialize = Deserializer(client_models) + self._serialize = Serializer() + self._deserialize = Deserializer() self._serialize.client_side_validation = False def send_request(self, request: HttpRequest, *, stream: bool = False, **kwargs: Any) -> HttpResponse: @@ -95,7 +95,7 @@ def send_request(self, request: HttpRequest, *, stream: bool = False, **kwargs: def close(self) -> None: self._client.close() - def __enter__(self) -> "QuestionAnsweringClient": + def __enter__(self) -> Self: self._client.__enter__() return self diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_configuration.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_configuration.py index c37d09345447..158cc45a4fe2 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_configuration.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_configuration.py @@ -2,36 +2,42 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -from typing import Any +from typing import Any, TYPE_CHECKING, Union from azure.core.credentials import AzureKeyCredential from azure.core.pipeline import policies from ._version import VERSION +if TYPE_CHECKING: + from azure.core.credentials import TokenCredential -class QuestionAnsweringClientConfiguration: # pylint: disable=too-many-instance-attributes,name-too-long + +class QuestionAnsweringClientConfiguration: # pylint: disable=too-many-instance-attributes """Configuration for QuestionAnsweringClient. Note that all parameters used to create this instance are saved as instance attributes. :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.api.cognitiveservices.azure.com). Required. + https://.api.cognitiveservices.azure.com). Required. :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. Required. - :type credential: ~azure.core.credentials.AzureKeyCredential - :keyword api_version: Api Version. Default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. + :param credential: Credential used to authenticate requests to the service. Is either a key + credential type or a token credential type. Required. + :type credential: ~azure.core.credentials.AzureKeyCredential or + ~azure.core.credentials.TokenCredential + :keyword api_version: The API version to use for this operation. Default value is + "2025-05-15-preview". Note that overriding this default value may result in unsupported + behavior. :paramtype api_version: str """ - def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) -> None: - api_version: str = kwargs.pop("api_version", "2021-10-01") + def __init__(self, endpoint: str, credential: Union[AzureKeyCredential, "TokenCredential"], **kwargs: Any) -> None: + api_version: str = kwargs.pop("api_version", "2025-05-15-preview") if endpoint is None: raise ValueError("Parameter 'endpoint' must not be None.") @@ -41,10 +47,18 @@ def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) self.endpoint = endpoint self.credential = credential self.api_version = api_version + self.credential_scopes = kwargs.pop("credential_scopes", ["https://cognitiveservices.azure.com/.default"]) kwargs.setdefault("sdk_moniker", "ai-language-questionanswering/{}".format(VERSION)) self.polling_interval = kwargs.get("polling_interval", 30) self._configure(**kwargs) + def _infer_policy(self, **kwargs): + if isinstance(self.credential, AzureKeyCredential): + return policies.AzureKeyCredentialPolicy(self.credential, "Ocp-Apim-Subscription-Key", **kwargs) + if hasattr(self.credential, "get_token"): + return policies.BearerTokenCredentialPolicy(self.credential, *self.credential_scopes, **kwargs) + raise TypeError(f"Unsupported credential: {self.credential}") + def _configure(self, **kwargs: Any) -> None: self.user_agent_policy = kwargs.get("user_agent_policy") or policies.UserAgentPolicy(**kwargs) self.headers_policy = kwargs.get("headers_policy") or policies.HeadersPolicy(**kwargs) @@ -56,6 +70,4 @@ def _configure(self, **kwargs: Any) -> None: self.retry_policy = kwargs.get("retry_policy") or policies.RetryPolicy(**kwargs) self.authentication_policy = kwargs.get("authentication_policy") if self.credential and not self.authentication_policy: - self.authentication_policy = policies.AzureKeyCredentialPolicy( - self.credential, "Ocp-Apim-Subscription-Key", **kwargs - ) + self.authentication_policy = self._infer_policy(**kwargs) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/__init__.py index 698e94b69dbb..d3b2eadf1b56 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/__init__.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/__init__.py @@ -2,17 +2,22 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position -from ._patch import QuestionAnsweringClientOperationsMixin +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import -from ._patch import patch_sdk as _patch_sdk +from ._operations import _QuestionAnsweringClientOperationsMixin # type: ignore # pylint: disable=unused-import -__all__ = [ - "QuestionAnsweringClientOperationsMixin", -] +from ._patch import __all__ as _patch_all +from ._patch import * +from ._patch import patch_sdk as _patch_sdk +__all__ = [] +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore _patch_sdk() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/_operations.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/_operations.py index 3f53de1dc162..f16a63f05be1 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/_operations.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/_operations.py @@ -1,20 +1,25 @@ -# pylint: disable=too-many-lines,too-many-statements +# pylint: disable=line-too-long,useless-suppression # coding=utf-8 # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +from collections.abc import MutableMapping from io import IOBase -from typing import Any, Callable, Dict, IO, Optional, TypeVar, Union, overload +import json +from typing import Any, Callable, IO, Optional, TypeVar, Union, overload +from azure.core import PipelineClient from azure.core.exceptions import ( ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, ResourceNotModifiedError, + StreamClosedError, + StreamConsumedError, map_error, ) from azure.core.pipeline import PipelineResponse @@ -23,11 +28,14 @@ from azure.core.utils import case_insensitive_dict from .. import models as _models -from .._serialization import Serializer -from .._vendor import QuestionAnsweringClientMixinABC +from .._configuration import QuestionAnsweringClientConfiguration +from .._utils.model_base import SdkJSONEncoder, _deserialize, _failsafe_deserialize +from .._utils.serialization import Serializer +from .._utils.utils import ClientMixinABC +JSON = MutableMapping[str, Any] T = TypeVar("T") -ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, dict[str, Any]], Any]] _SERIALIZER = Serializer() _SERIALIZER.client_side_validation = False @@ -40,16 +48,16 @@ def build_question_answering_get_answers_request( # pylint: disable=name-too-lo _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2025-05-15-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/:query-knowledgebases" # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") _params["projectName"] = _SERIALIZER.query("project_name", project_name, "str") _params["deploymentName"] = _SERIALIZER.query("deployment_name", deployment_name, "str") - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") # Construct headers if content_type is not None: @@ -66,7 +74,7 @@ def build_question_answering_get_answers_from_text_request( # pylint: disable=n _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2025-05-15-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -83,11 +91,14 @@ def build_question_answering_get_answers_from_text_request( # pylint: disable=n return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -class QuestionAnsweringClientOperationsMixin(QuestionAnsweringClientMixinABC): +class _QuestionAnsweringClientOperationsMixin( + ClientMixinABC[PipelineClient[HttpRequest, HttpResponse], QuestionAnsweringClientConfiguration] +): + @overload def get_answers( self, - options: _models.AnswersOptions, + knowledge_base_query_options: _models.AnswersOptions, *, project_name: str, deployment_name: str, @@ -96,10 +107,8 @@ def get_answers( ) -> _models.AnswersResult: """Answers the specified question using your knowledge base. - Answers the specified question using your knowledge base. - - :param options: Post body of the request. Required. - :type options: ~azure.ai.language.questionanswering.models.AnswersOptions + :param knowledge_base_query_options: Post body of the request. Required. + :type knowledge_base_query_options: ~azure.ai.language.questionanswering.models.AnswersOptions :keyword project_name: The name of the project to use. Required. :paramtype project_name: str :keyword deployment_name: The name of the specific deployment of the project to use. Required. @@ -107,7 +116,7 @@ def get_answers( :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: AnswersResult + :return: AnswersResult. The AnswersResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersResult :raises ~azure.core.exceptions.HttpResponseError: """ @@ -115,7 +124,7 @@ def get_answers( @overload def get_answers( self, - options: IO[bytes], + knowledge_base_query_options: JSON, *, project_name: str, deployment_name: str, @@ -124,10 +133,34 @@ def get_answers( ) -> _models.AnswersResult: """Answers the specified question using your knowledge base. - Answers the specified question using your knowledge base. + :param knowledge_base_query_options: Post body of the request. Required. + :type knowledge_base_query_options: JSON + :keyword project_name: The name of the project to use. Required. + :paramtype project_name: str + :keyword deployment_name: The name of the specific deployment of the project to use. Required. + :paramtype deployment_name: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: AnswersResult. The AnswersResult is compatible with MutableMapping + :rtype: ~azure.ai.language.questionanswering.models.AnswersResult + :raises ~azure.core.exceptions.HttpResponseError: + """ - :param options: Post body of the request. Required. - :type options: IO[bytes] + @overload + def get_answers( + self, + knowledge_base_query_options: IO[bytes], + *, + project_name: str, + deployment_name: str, + content_type: str = "application/json", + **kwargs: Any, + ) -> _models.AnswersResult: + """Answers the specified question using your knowledge base. + + :param knowledge_base_query_options: Post body of the request. Required. + :type knowledge_base_query_options: IO[bytes] :keyword project_name: The name of the project to use. Required. :paramtype project_name: str :keyword deployment_name: The name of the specific deployment of the project to use. Required. @@ -135,7 +168,7 @@ def get_answers( :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: AnswersResult + :return: AnswersResult. The AnswersResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersResult :raises ~azure.core.exceptions.HttpResponseError: """ @@ -143,7 +176,7 @@ def get_answers( @distributed_trace def get_answers( self, - options: Union[_models.AnswersOptions, IO[bytes]], + knowledge_base_query_options: Union[_models.AnswersOptions, JSON, IO[bytes]], *, project_name: str, deployment_name: str, @@ -151,20 +184,19 @@ def get_answers( ) -> _models.AnswersResult: """Answers the specified question using your knowledge base. - Answers the specified question using your knowledge base. - - :param options: Post body of the request. Is either a AnswersOptions type or a IO[bytes] type. - Required. - :type options: ~azure.ai.language.questionanswering.models.AnswersOptions or IO[bytes] + :param knowledge_base_query_options: Post body of the request. Is one of the following types: + AnswersOptions, JSON, IO[bytes] Required. + :type knowledge_base_query_options: ~azure.ai.language.questionanswering.models.AnswersOptions + or JSON or IO[bytes] :keyword project_name: The name of the project to use. Required. :paramtype project_name: str :keyword deployment_name: The name of the specific deployment of the project to use. Required. :paramtype deployment_name: str - :return: AnswersResult + :return: AnswersResult. The AnswersResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersResult :raises ~azure.core.exceptions.HttpResponseError: """ - error_map = { + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, @@ -179,19 +211,17 @@ def get_answers( cls: ClsType[_models.AnswersResult] = kwargs.pop("cls", None) content_type = content_type or "application/json" - _json = None _content = None - if isinstance(options, (IOBase, bytes)): - _content = options + if isinstance(knowledge_base_query_options, (IOBase, bytes)): + _content = knowledge_base_query_options else: - _json = self._serialize.body(options, "AnswersOptions") + _content = json.dumps(knowledge_base_query_options, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_question_answering_get_answers_request( project_name=project_name, deployment_name=deployment_name, content_type=content_type, api_version=self._config.api_version, - json=_json, content=_content, headers=_headers, params=_params, @@ -201,7 +231,7 @@ def get_answers( } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _stream = kwargs.pop("stream", False) pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) @@ -210,12 +240,18 @@ def get_answers( if response.status_code not in [200]: if _stream: - response.read() # Load the body in memory and close the socket + try: + response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass map_error(status_code=response.status_code, response=response, error_map=error_map) - error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + error = _failsafe_deserialize(_models.ErrorResponse, response) raise HttpResponseError(response=response, model=error) - deserialized = self._deserialize("AnswersResult", pipeline_response) + if _stream: + deserialized = response.iter_bytes() + else: + deserialized = _deserialize(_models.AnswersResult, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -224,56 +260,71 @@ def get_answers( @overload def get_answers_from_text( - self, options: _models.AnswersFromTextOptions, *, content_type: str = "application/json", **kwargs: Any + self, + text_query_options: _models.AnswersFromTextOptions, + *, + content_type: str = "application/json", + **kwargs: Any, ) -> _models.AnswersFromTextResult: """Answers the specified question using the provided text in the body. - Answers the specified question using the provided text in the body. - - :param options: Post body of the request. Required. - :type options: ~azure.ai.language.questionanswering.models.AnswersFromTextOptions + :param text_query_options: Post body of the request. Required. + :type text_query_options: ~azure.ai.language.questionanswering.models.AnswersFromTextOptions :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: AnswersFromTextResult + :return: AnswersFromTextResult. The AnswersFromTextResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersFromTextResult :raises ~azure.core.exceptions.HttpResponseError: """ @overload def get_answers_from_text( - self, options: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + self, text_query_options: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.AnswersFromTextResult: """Answers the specified question using the provided text in the body. - Answers the specified question using the provided text in the body. + :param text_query_options: Post body of the request. Required. + :type text_query_options: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: AnswersFromTextResult. The AnswersFromTextResult is compatible with MutableMapping + :rtype: ~azure.ai.language.questionanswering.models.AnswersFromTextResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def get_answers_from_text( + self, text_query_options: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.AnswersFromTextResult: + """Answers the specified question using the provided text in the body. - :param options: Post body of the request. Required. - :type options: IO[bytes] + :param text_query_options: Post body of the request. Required. + :type text_query_options: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: AnswersFromTextResult + :return: AnswersFromTextResult. The AnswersFromTextResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersFromTextResult :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace def get_answers_from_text( - self, options: Union[_models.AnswersFromTextOptions, IO[bytes]], **kwargs: Any + self, text_query_options: Union[_models.AnswersFromTextOptions, JSON, IO[bytes]], **kwargs: Any ) -> _models.AnswersFromTextResult: """Answers the specified question using the provided text in the body. - Answers the specified question using the provided text in the body. - - :param options: Post body of the request. Is either a AnswersFromTextOptions type or a - IO[bytes] type. Required. - :type options: ~azure.ai.language.questionanswering.models.AnswersFromTextOptions or IO[bytes] - :return: AnswersFromTextResult + :param text_query_options: Post body of the request. Is one of the following types: + AnswersFromTextOptions, JSON, IO[bytes] Required. + :type text_query_options: ~azure.ai.language.questionanswering.models.AnswersFromTextOptions or + JSON or IO[bytes] + :return: AnswersFromTextResult. The AnswersFromTextResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersFromTextResult :raises ~azure.core.exceptions.HttpResponseError: """ - error_map = { + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, @@ -288,17 +339,15 @@ def get_answers_from_text( cls: ClsType[_models.AnswersFromTextResult] = kwargs.pop("cls", None) content_type = content_type or "application/json" - _json = None _content = None - if isinstance(options, (IOBase, bytes)): - _content = options + if isinstance(text_query_options, (IOBase, bytes)): + _content = text_query_options else: - _json = self._serialize.body(options, "AnswersFromTextOptions") + _content = json.dumps(text_query_options, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_question_answering_get_answers_from_text_request( content_type=content_type, api_version=self._config.api_version, - json=_json, content=_content, headers=_headers, params=_params, @@ -308,7 +357,7 @@ def get_answers_from_text( } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _stream = kwargs.pop("stream", False) pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access _request, stream=_stream, **kwargs ) @@ -317,12 +366,18 @@ def get_answers_from_text( if response.status_code not in [200]: if _stream: - response.read() # Load the body in memory and close the socket + try: + response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass map_error(status_code=response.status_code, response=response, error_map=error_map) - error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + error = _failsafe_deserialize(_models.ErrorResponse, response) raise HttpResponseError(response=response, model=error) - deserialized = self._deserialize("AnswersFromTextResult", pipeline_response) + if _stream: + deserialized = response.iter_bytes() + else: + deserialized = _deserialize(_models.AnswersFromTextResult, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/_patch.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/_patch.py index bd34dd7e9987..60951c043425 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/_patch.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_operations/_patch.py @@ -1,7 +1,9 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ +# pylint: disable=line-too-long,useless-suppression +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- """Customize generated code here. Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize @@ -10,7 +12,7 @@ import copy from azure.core.tracing.decorator import distributed_trace -from ._operations import QuestionAnsweringClientOperationsMixin as QuestionAnsweringClientOperationsMixinGenerated +from ._operations import _QuestionAnsweringClientOperationsMixin as QuestionAnsweringClientOperationsMixinGenerated from ..models import ( AnswersOptions, AnswersFromTextOptions, @@ -90,12 +92,23 @@ def _handle_metadata_filter_conversion(options_input): in_class = False if not metadata_input: return options - try: - if any(t for t in metadata_input if len(t) != 2): - raise ValueError("'metadata' must be a sequence of key-value tuples.") - except TypeError as exc: - raise ValueError("'metadata' must be a sequence of key-value tuples.") from exc - metadata_modified = [{"key": m[0], "value": m[1]} for m in metadata_input] + + # Handle both MetadataRecord objects and tuples + metadata_modified = [] + for m in metadata_input: + if hasattr(m, "key") and hasattr(m, "value"): + # MetadataRecord object + metadata_modified.append({"key": m.key, "value": m.value}) + else: + # Assume it's a tuple or sequence + try: + if len(m) != 2: + raise ValueError("'metadata' must be a sequence of key-value tuples.") + metadata_modified.append({"key": m[0], "value": m[1]}) + except (TypeError, AttributeError) as exc: + raise ValueError( + "'metadata' must be a sequence of key-value tuples or MetadataRecord objects." + ) from exc if in_class: filters.metadata_filter.metadata = metadata_modified else: @@ -142,15 +155,17 @@ def _get_answers_from_text_prepare_options( return options, kwargs -class QuestionAnsweringClientOperationsMixin(QuestionAnsweringClientOperationsMixinGenerated): +class _QuestionAnsweringClientOperationsMixin(QuestionAnsweringClientOperationsMixinGenerated): @overload # type: ignore # https://github.com/Azure/azure-sdk-for-python/issues/26621 + # pylint: disable=arguments-renamed def get_answers( self, options: AnswersOptions, *, project_name: str, deployment_name: str, **kwargs: Any ) -> AnswersResult: """Answers the specified question using your knowledge base. - :param options: Positional only. POST body of the request. Provide either `options`, OR - individual keyword arguments. If both are provided, only the options object will be used. + :param options: Positional only. POST body of the request. Provide either + `options`, OR individual keyword arguments. If both are provided, only the + options object will be used. :type options: ~azure.ai.language.questionanswering.models.AnswersOptions :keyword project_name: The name of the knowledge base project to use. :paramtype project_name: str @@ -214,12 +229,10 @@ def get_answers( # pylint: disable=arguments-differ """ # pylint ignore b/c with overloads we need to doc ALL the params in the impl for them to show up in docs - # pylint: disable=docstring-keyword-should-match-keyword-only,docstring-missing-param,docstring-should-be-keyword + # pylint: disable=docstring-keyword-should-match-keyword-only,docstring-missing-param,docstring-should-be-keyword,arguments-renamed @distributed_trace def get_answers( # pyright: ignore[reportIncompatibleMethodOverride] - self, - *args: AnswersOptions, - **kwargs: Any + self, *args: AnswersOptions, **kwargs: Any ) -> AnswersResult: """Answers the specified question using your knowledge base. @@ -296,20 +309,18 @@ def get_answers_from_text( # pylint: disable=arguments-differ :paramtype question: str :keyword text_documents: Text records to be searched for given question. :paramtype text_documents: list[str or ~azure.ai.language.questionanswering.models.TextDocument] - :keyword language: Language of the text records. This is BCP-47 representation of a language. - For example, use "en" for English; "es" for Spanish etc. If not set, use "en" for English as - default. + :keyword language: BCP-47 language tag (e.g. "en", "es"). If omitted, the client does not set a + value and the service will apply its own default (currently English). :paramtype language: str :return: AnswersFromTextResult :rtype: ~azure.ai.language.questionanswering.models.AnswersFromTextResult :raises ~azure.core.exceptions.HttpResponseError: """ + # pylint: disable=arguments-renamed @distributed_trace def get_answers_from_text( # pyright: ignore[reportIncompatibleMethodOverride] - self, - *args: AnswersFromTextOptions, - **kwargs: Any + self, *args: AnswersFromTextOptions, **kwargs: Any ) -> AnswersFromTextResult: """Answers the specified question using the provided text in the body. @@ -337,14 +348,16 @@ def get_answers_from_text( # pyright: ignore[reportIncompatibleMethodOverride] :dedent: 4 :caption: Answers the specified question using the provided text. """ + # Use only explicit user-provided language (if any); no hidden default attribute + _explicit_language = kwargs.pop("language", None) options, kwargs = _get_answers_from_text_prepare_options( - *args, language=kwargs.pop("language", self._default_language), **kwargs # type: ignore + *args, language=_explicit_language, **kwargs # type: ignore ) return super().get_answers_from_text(options, **kwargs) # type: ignore __all__: List[str] = [ - "QuestionAnsweringClientOperationsMixin" + "_QuestionAnsweringClientOperationsMixin" ] # Add all objects you want publicly available to users at this package level diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_patch.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_patch.py index 1dd15446960a..87676c65a8f0 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_patch.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_patch.py @@ -3,69 +3,19 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------- +"""Customize generated code here. -from typing import Union, Any -from azure.core.credentials import AzureKeyCredential, TokenCredential -from azure.core.pipeline.policies import AzureKeyCredentialPolicy, BearerTokenCredentialPolicy -from ._client import QuestionAnsweringClient as QuestionAnsweringClientGenerated +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" -def _authentication_policy(credential, **kwargs): - if credential is None: - raise ValueError("Parameter 'credential' must not be None.") - if isinstance(credential, AzureKeyCredential): - authentication_policy = AzureKeyCredentialPolicy( - name="Ocp-Apim-Subscription-Key", credential=credential, **kwargs - ) - elif hasattr(credential, "get_token"): - authentication_policy = BearerTokenCredentialPolicy( - credential, *kwargs.pop("credential_scopes", ["https://cognitiveservices.azure.com/.default"]), **kwargs - ) - else: - raise TypeError( - "Unsupported credential: {}. Use an instance of AzureKeyCredential " - "or a token credential from azure.identity".format(type(credential)) - ) - return authentication_policy - - -class QuestionAnsweringClient(QuestionAnsweringClientGenerated): - """The language service API is a suite of natural language processing (NLP) skills built with best-in-class - Microsoft machine learning algorithms. - - The API can be used to analyze unstructured text for tasks such as sentiment - analysis, key phrase extraction, language detection and question answering. - Further documentation can be found in https://learn.microsoft.com/azure/cognitive-services/language-service/overview - - :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.cognitiveservices.azure.com). - :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. - This can be the an instance of AzureKeyCredential if using a Language API key - or a token credential from :mod:`azure.identity`. - :type credential: ~azure.core.credentials.AzureKeyCredential or ~azure.core.credentials.TokenCredential - :keyword str default_language: Sets the default language to use for all operations. - :keyword api_version: Api Version. Default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. - :paramtype api_version: str - """ - - def __init__(self, endpoint: str, credential: Union[AzureKeyCredential, TokenCredential], **kwargs: Any) -> None: - try: - endpoint = endpoint.rstrip("/") - except AttributeError as exc: - raise ValueError("Parameter 'endpoint' must be a string.") from exc - super().__init__( - endpoint=endpoint, - credential=credential, # type: ignore - authentication_policy=kwargs.pop("authentication_policy", _authentication_policy(credential, **kwargs)), - **kwargs - ) - self._default_language = kwargs.pop("default_language", None) +__all__: list[str] = [] # Add all objects you want publicly available to users at this package level def patch_sdk(): - pass + """Do not remove from this file. - -__all__ = ["QuestionAnsweringClient"] + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_version.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/__init__.py similarity index 80% rename from sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_version.py rename to sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/__init__.py index 924647bfeefc..8026245c2abc 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_version.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/__init__.py @@ -1,9 +1,6 @@ -# coding=utf-8 # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- - -VERSION = "1.1.1" diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/model_base.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/model_base.py new file mode 100644 index 000000000000..12926fa98dcf --- /dev/null +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/model_base.py @@ -0,0 +1,1237 @@ +# pylint: disable=line-too-long,useless-suppression,too-many-lines +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +# pylint: disable=protected-access, broad-except + +import copy +import calendar +import decimal +import functools +import sys +import logging +import base64 +import re +import typing +import enum +import email.utils +from datetime import datetime, date, time, timedelta, timezone +from json import JSONEncoder +import xml.etree.ElementTree as ET +from collections.abc import MutableMapping +from typing_extensions import Self +import isodate +from azure.core.exceptions import DeserializationError +from azure.core import CaseInsensitiveEnumMeta +from azure.core.pipeline import PipelineResponse +from azure.core.serialization import _Null +from azure.core.rest import HttpResponse + +_LOGGER = logging.getLogger(__name__) + +__all__ = ["SdkJSONEncoder", "Model", "rest_field", "rest_discriminator"] + +TZ_UTC = timezone.utc +_T = typing.TypeVar("_T") + + +def _timedelta_as_isostr(td: timedelta) -> str: + """Converts a datetime.timedelta object into an ISO 8601 formatted string, e.g. 'P4DT12H30M05S' + + Function adapted from the Tin Can Python project: https://github.com/RusticiSoftware/TinCanPython + + :param timedelta td: The timedelta to convert + :rtype: str + :return: ISO8601 version of this timedelta + """ + + # Split seconds to larger units + seconds = td.total_seconds() + minutes, seconds = divmod(seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + + days, hours, minutes = list(map(int, (days, hours, minutes))) + seconds = round(seconds, 6) + + # Build date + date_str = "" + if days: + date_str = "%sD" % days + + if hours or minutes or seconds: + # Build time + time_str = "T" + + # Hours + bigger_exists = date_str or hours + if bigger_exists: + time_str += "{:02}H".format(hours) + + # Minutes + bigger_exists = bigger_exists or minutes + if bigger_exists: + time_str += "{:02}M".format(minutes) + + # Seconds + try: + if seconds.is_integer(): + seconds_string = "{:02}".format(int(seconds)) + else: + # 9 chars long w/ leading 0, 6 digits after decimal + seconds_string = "%09.6f" % seconds + # Remove trailing zeros + seconds_string = seconds_string.rstrip("0") + except AttributeError: # int.is_integer() raises + seconds_string = "{:02}".format(seconds) + + time_str += "{}S".format(seconds_string) + else: + time_str = "" + + return "P" + date_str + time_str + + +def _serialize_bytes(o, format: typing.Optional[str] = None) -> str: + encoded = base64.b64encode(o).decode() + if format == "base64url": + return encoded.strip("=").replace("+", "-").replace("/", "_") + return encoded + + +def _serialize_datetime(o, format: typing.Optional[str] = None): + if hasattr(o, "year") and hasattr(o, "hour"): + if format == "rfc7231": + return email.utils.format_datetime(o, usegmt=True) + if format == "unix-timestamp": + return int(calendar.timegm(o.utctimetuple())) + + # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set) + if not o.tzinfo: + iso_formatted = o.replace(tzinfo=TZ_UTC).isoformat() + else: + iso_formatted = o.astimezone(TZ_UTC).isoformat() + # Replace the trailing "+00:00" UTC offset with "Z" (RFC 3339: https://www.ietf.org/rfc/rfc3339.txt) + return iso_formatted.replace("+00:00", "Z") + # Next try datetime.date or datetime.time + return o.isoformat() + + +def _is_readonly(p): + try: + return p._visibility == ["read"] + except AttributeError: + return False + + +class SdkJSONEncoder(JSONEncoder): + """A JSON encoder that's capable of serializing datetime objects and bytes.""" + + def __init__(self, *args, exclude_readonly: bool = False, format: typing.Optional[str] = None, **kwargs): + super().__init__(*args, **kwargs) + self.exclude_readonly = exclude_readonly + self.format = format + + def default(self, o): # pylint: disable=too-many-return-statements + if _is_model(o): + if self.exclude_readonly: + readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)] + return {k: v for k, v in o.items() if k not in readonly_props} + return dict(o.items()) + try: + return super(SdkJSONEncoder, self).default(o) + except TypeError: + if isinstance(o, _Null): + return None + if isinstance(o, decimal.Decimal): + return float(o) + if isinstance(o, (bytes, bytearray)): + return _serialize_bytes(o, self.format) + try: + # First try datetime.datetime + return _serialize_datetime(o, self.format) + except AttributeError: + pass + # Last, try datetime.timedelta + try: + return _timedelta_as_isostr(o) + except AttributeError: + # This will be raised when it hits value.total_seconds in the method above + pass + return super(SdkJSONEncoder, self).default(o) + + +_VALID_DATE = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}" + r"\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?") +_VALID_RFC7231 = re.compile( + r"(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s\d{2}\s" + r"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT" +) + + +def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime: + """Deserialize ISO-8601 formatted string into Datetime object. + + :param str attr: response string to be deserialized. + :rtype: ~datetime.datetime + :returns: The datetime object from that input + """ + if isinstance(attr, datetime): + # i'm already deserialized + return attr + attr = attr.upper() + match = _VALID_DATE.match(attr) + if not match: + raise ValueError("Invalid datetime string: " + attr) + + check_decimal = attr.split(".") + if len(check_decimal) > 1: + decimal_str = "" + for digit in check_decimal[1]: + if digit.isdigit(): + decimal_str += digit + else: + break + if len(decimal_str) > 6: + attr = attr.replace(decimal_str, decimal_str[0:6]) + + date_obj = isodate.parse_datetime(attr) + test_utc = date_obj.utctimetuple() + if test_utc.tm_year > 9999 or test_utc.tm_year < 1: + raise OverflowError("Hit max or min date") + return date_obj + + +def _deserialize_datetime_rfc7231(attr: typing.Union[str, datetime]) -> datetime: + """Deserialize RFC7231 formatted string into Datetime object. + + :param str attr: response string to be deserialized. + :rtype: ~datetime.datetime + :returns: The datetime object from that input + """ + if isinstance(attr, datetime): + # i'm already deserialized + return attr + match = _VALID_RFC7231.match(attr) + if not match: + raise ValueError("Invalid datetime string: " + attr) + + return email.utils.parsedate_to_datetime(attr) + + +def _deserialize_datetime_unix_timestamp(attr: typing.Union[float, datetime]) -> datetime: + """Deserialize unix timestamp into Datetime object. + + :param str attr: response string to be deserialized. + :rtype: ~datetime.datetime + :returns: The datetime object from that input + """ + if isinstance(attr, datetime): + # i'm already deserialized + return attr + return datetime.fromtimestamp(attr, TZ_UTC) + + +def _deserialize_date(attr: typing.Union[str, date]) -> date: + """Deserialize ISO-8601 formatted string into Date object. + :param str attr: response string to be deserialized. + :rtype: date + :returns: The date object from that input + """ + # This must NOT use defaultmonth/defaultday. Using None ensure this raises an exception. + if isinstance(attr, date): + return attr + return isodate.parse_date(attr, defaultmonth=None, defaultday=None) # type: ignore + + +def _deserialize_time(attr: typing.Union[str, time]) -> time: + """Deserialize ISO-8601 formatted string into time object. + + :param str attr: response string to be deserialized. + :rtype: datetime.time + :returns: The time object from that input + """ + if isinstance(attr, time): + return attr + return isodate.parse_time(attr) + + +def _deserialize_bytes(attr): + if isinstance(attr, (bytes, bytearray)): + return attr + return bytes(base64.b64decode(attr)) + + +def _deserialize_bytes_base64(attr): + if isinstance(attr, (bytes, bytearray)): + return attr + padding = "=" * (3 - (len(attr) + 3) % 4) # type: ignore + attr = attr + padding # type: ignore + encoded = attr.replace("-", "+").replace("_", "/") + return bytes(base64.b64decode(encoded)) + + +def _deserialize_duration(attr): + if isinstance(attr, timedelta): + return attr + return isodate.parse_duration(attr) + + +def _deserialize_decimal(attr): + if isinstance(attr, decimal.Decimal): + return attr + return decimal.Decimal(str(attr)) + + +def _deserialize_int_as_str(attr): + if isinstance(attr, int): + return attr + return int(attr) + + +_DESERIALIZE_MAPPING = { + datetime: _deserialize_datetime, + date: _deserialize_date, + time: _deserialize_time, + bytes: _deserialize_bytes, + bytearray: _deserialize_bytes, + timedelta: _deserialize_duration, + typing.Any: lambda x: x, + decimal.Decimal: _deserialize_decimal, +} + +_DESERIALIZE_MAPPING_WITHFORMAT = { + "rfc3339": _deserialize_datetime, + "rfc7231": _deserialize_datetime_rfc7231, + "unix-timestamp": _deserialize_datetime_unix_timestamp, + "base64": _deserialize_bytes, + "base64url": _deserialize_bytes_base64, +} + + +def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None): + if annotation is int and rf and rf._format == "str": + return _deserialize_int_as_str + if rf and rf._format: + return _DESERIALIZE_MAPPING_WITHFORMAT.get(rf._format) + return _DESERIALIZE_MAPPING.get(annotation) # pyright: ignore + + +def _get_type_alias_type(module_name: str, alias_name: str): + types = { + k: v + for k, v in sys.modules[module_name].__dict__.items() + if isinstance(v, typing._GenericAlias) # type: ignore + } + if alias_name not in types: + return alias_name + return types[alias_name] + + +def _get_model(module_name: str, model_name: str): + models = {k: v for k, v in sys.modules[module_name].__dict__.items() if isinstance(v, type)} + module_end = module_name.rsplit(".", 1)[0] + models.update({k: v for k, v in sys.modules[module_end].__dict__.items() if isinstance(v, type)}) + if isinstance(model_name, str): + model_name = model_name.split(".")[-1] + if model_name not in models: + return model_name + return models[model_name] + + +_UNSET = object() + + +class _MyMutableMapping(MutableMapping[str, typing.Any]): + def __init__(self, data: dict[str, typing.Any]) -> None: + self._data = data + + def __contains__(self, key: typing.Any) -> bool: + return key in self._data + + def __getitem__(self, key: str) -> typing.Any: + return self._data.__getitem__(key) + + def __setitem__(self, key: str, value: typing.Any) -> None: + self._data.__setitem__(key, value) + + def __delitem__(self, key: str) -> None: + self._data.__delitem__(key) + + def __iter__(self) -> typing.Iterator[typing.Any]: + return self._data.__iter__() + + def __len__(self) -> int: + return self._data.__len__() + + def __ne__(self, other: typing.Any) -> bool: + return not self.__eq__(other) + + def keys(self) -> typing.KeysView[str]: + """ + :returns: a set-like object providing a view on D's keys + :rtype: ~typing.KeysView + """ + return self._data.keys() + + def values(self) -> typing.ValuesView[typing.Any]: + """ + :returns: an object providing a view on D's values + :rtype: ~typing.ValuesView + """ + return self._data.values() + + def items(self) -> typing.ItemsView[str, typing.Any]: + """ + :returns: set-like object providing a view on D's items + :rtype: ~typing.ItemsView + """ + return self._data.items() + + def get(self, key: str, default: typing.Any = None) -> typing.Any: + """ + Get the value for key if key is in the dictionary, else default. + :param str key: The key to look up. + :param any default: The value to return if key is not in the dictionary. Defaults to None + :returns: D[k] if k in D, else d. + :rtype: any + """ + try: + return self[key] + except KeyError: + return default + + @typing.overload + def pop(self, key: str) -> typing.Any: ... # pylint: disable=arguments-differ + + @typing.overload + def pop(self, key: str, default: _T) -> _T: ... # pylint: disable=signature-differs + + @typing.overload + def pop(self, key: str, default: typing.Any) -> typing.Any: ... # pylint: disable=signature-differs + + def pop(self, key: str, default: typing.Any = _UNSET) -> typing.Any: + """ + Removes specified key and return the corresponding value. + :param str key: The key to pop. + :param any default: The value to return if key is not in the dictionary + :returns: The value corresponding to the key. + :rtype: any + :raises KeyError: If key is not found and default is not given. + """ + if default is _UNSET: + return self._data.pop(key) + return self._data.pop(key, default) + + def popitem(self) -> tuple[str, typing.Any]: + """ + Removes and returns some (key, value) pair + :returns: The (key, value) pair. + :rtype: tuple + :raises KeyError: if D is empty. + """ + return self._data.popitem() + + def clear(self) -> None: + """ + Remove all items from D. + """ + self._data.clear() + + def update(self, *args: typing.Any, **kwargs: typing.Any) -> None: # pylint: disable=arguments-differ + """ + Updates D from mapping/iterable E and F. + :param any args: Either a mapping object or an iterable of key-value pairs. + """ + self._data.update(*args, **kwargs) + + @typing.overload + def setdefault(self, key: str, default: None = None) -> None: ... + + @typing.overload + def setdefault(self, key: str, default: typing.Any) -> typing.Any: ... # pylint: disable=signature-differs + + def setdefault(self, key: str, default: typing.Any = _UNSET) -> typing.Any: + """ + Same as calling D.get(k, d), and setting D[k]=d if k not found + :param str key: The key to look up. + :param any default: The value to set if key is not in the dictionary + :returns: D[k] if k in D, else d. + :rtype: any + """ + if default is _UNSET: + return self._data.setdefault(key) + return self._data.setdefault(key, default) + + def __eq__(self, other: typing.Any) -> bool: + try: + other_model = self.__class__(other) + except Exception: + return False + return self._data == other_model._data + + def __repr__(self) -> str: + return str(self._data) + + +def _is_model(obj: typing.Any) -> bool: + return getattr(obj, "_is_model", False) + + +def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-many-return-statements + if isinstance(o, list): + return [_serialize(x, format) for x in o] + if isinstance(o, dict): + return {k: _serialize(v, format) for k, v in o.items()} + if isinstance(o, set): + return {_serialize(x, format) for x in o} + if isinstance(o, tuple): + return tuple(_serialize(x, format) for x in o) + if isinstance(o, (bytes, bytearray)): + return _serialize_bytes(o, format) + if isinstance(o, decimal.Decimal): + return float(o) + if isinstance(o, enum.Enum): + return o.value + if isinstance(o, int): + if format == "str": + return str(o) + return o + try: + # First try datetime.datetime + return _serialize_datetime(o, format) + except AttributeError: + pass + # Last, try datetime.timedelta + try: + return _timedelta_as_isostr(o) + except AttributeError: + # This will be raised when it hits value.total_seconds in the method above + pass + return o + + +def _get_rest_field(attr_to_rest_field: dict[str, "_RestField"], rest_name: str) -> typing.Optional["_RestField"]: + try: + return next(rf for rf in attr_to_rest_field.values() if rf._rest_name == rest_name) + except StopIteration: + return None + + +def _create_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typing.Any: + if not rf: + return _serialize(value, None) + if rf._is_multipart_file_input: + return value + if rf._is_model: + return _deserialize(rf._type, value) + if isinstance(value, ET.Element): + value = _deserialize(rf._type, value) + return _serialize(value, rf._format) + + +class Model(_MyMutableMapping): + _is_model = True + # label whether current class's _attr_to_rest_field has been calculated + # could not see _attr_to_rest_field directly because subclass inherits it from parent class + _calculated: set[str] = set() + + def __init__(self, *args: typing.Any, **kwargs: typing.Any) -> None: + class_name = self.__class__.__name__ + if len(args) > 1: + raise TypeError(f"{class_name}.__init__() takes 2 positional arguments but {len(args) + 1} were given") + dict_to_pass = { + rest_field._rest_name: rest_field._default + for rest_field in self._attr_to_rest_field.values() + if rest_field._default is not _UNSET + } + if args: # pylint: disable=too-many-nested-blocks + if isinstance(args[0], ET.Element): + existed_attr_keys = [] + model_meta = getattr(self, "_xml", {}) + + for rf in self._attr_to_rest_field.values(): + prop_meta = getattr(rf, "_xml", {}) + xml_name = prop_meta.get("name", rf._rest_name) + xml_ns = prop_meta.get("ns", model_meta.get("ns", None)) + if xml_ns: + xml_name = "{" + xml_ns + "}" + xml_name + + # attribute + if prop_meta.get("attribute", False) and args[0].get(xml_name) is not None: + existed_attr_keys.append(xml_name) + dict_to_pass[rf._rest_name] = _deserialize(rf._type, args[0].get(xml_name)) + continue + + # unwrapped element is array + if prop_meta.get("unwrapped", False): + # unwrapped array could either use prop items meta/prop meta + if prop_meta.get("itemsName"): + xml_name = prop_meta.get("itemsName") + xml_ns = prop_meta.get("itemNs") + if xml_ns: + xml_name = "{" + xml_ns + "}" + xml_name + items = args[0].findall(xml_name) # pyright: ignore + if len(items) > 0: + existed_attr_keys.append(xml_name) + dict_to_pass[rf._rest_name] = _deserialize(rf._type, items) + continue + + # text element is primitive type + if prop_meta.get("text", False): + if args[0].text is not None: + dict_to_pass[rf._rest_name] = _deserialize(rf._type, args[0].text) + continue + + # wrapped element could be normal property or array, it should only have one element + item = args[0].find(xml_name) + if item is not None: + existed_attr_keys.append(xml_name) + dict_to_pass[rf._rest_name] = _deserialize(rf._type, item) + + # rest thing is additional properties + for e in args[0]: + if e.tag not in existed_attr_keys: + dict_to_pass[e.tag] = _convert_element(e) + else: + dict_to_pass.update( + {k: _create_value(_get_rest_field(self._attr_to_rest_field, k), v) for k, v in args[0].items()} + ) + else: + non_attr_kwargs = [k for k in kwargs if k not in self._attr_to_rest_field] + if non_attr_kwargs: + # actual type errors only throw the first wrong keyword arg they see, so following that. + raise TypeError(f"{class_name}.__init__() got an unexpected keyword argument '{non_attr_kwargs[0]}'") + dict_to_pass.update( + { + self._attr_to_rest_field[k]._rest_name: _create_value(self._attr_to_rest_field[k], v) + for k, v in kwargs.items() + if v is not None + } + ) + super().__init__(dict_to_pass) + + def copy(self) -> "Model": + return Model(self.__dict__) + + def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> Self: + if f"{cls.__module__}.{cls.__qualname__}" not in cls._calculated: + # we know the last nine classes in mro are going to be 'Model', '_MyMutableMapping', 'MutableMapping', + # 'Mapping', 'Collection', 'Sized', 'Iterable', 'Container' and 'object' + mros = cls.__mro__[:-9][::-1] # ignore parents, and reverse the mro order + attr_to_rest_field: dict[str, _RestField] = { # map attribute name to rest_field property + k: v for mro_class in mros for k, v in mro_class.__dict__.items() if k[0] != "_" and hasattr(v, "_type") + } + annotations = { + k: v + for mro_class in mros + if hasattr(mro_class, "__annotations__") + for k, v in mro_class.__annotations__.items() + } + for attr, rf in attr_to_rest_field.items(): + rf._module = cls.__module__ + if not rf._type: + rf._type = rf._get_deserialize_callable_from_annotation(annotations.get(attr, None)) + if not rf._rest_name_input: + rf._rest_name_input = attr + cls._attr_to_rest_field: dict[str, _RestField] = dict(attr_to_rest_field.items()) + cls._calculated.add(f"{cls.__module__}.{cls.__qualname__}") + + return super().__new__(cls) + + def __init_subclass__(cls, discriminator: typing.Optional[str] = None) -> None: + for base in cls.__bases__: + if hasattr(base, "__mapping__"): + base.__mapping__[discriminator or cls.__name__] = cls # type: ignore + + @classmethod + def _get_discriminator(cls, exist_discriminators) -> typing.Optional["_RestField"]: + for v in cls.__dict__.values(): + if isinstance(v, _RestField) and v._is_discriminator and v._rest_name not in exist_discriminators: + return v + return None + + @classmethod + def _deserialize(cls, data, exist_discriminators): + if not hasattr(cls, "__mapping__"): + return cls(data) + discriminator = cls._get_discriminator(exist_discriminators) + if discriminator is None: + return cls(data) + exist_discriminators.append(discriminator._rest_name) + if isinstance(data, ET.Element): + model_meta = getattr(cls, "_xml", {}) + prop_meta = getattr(discriminator, "_xml", {}) + xml_name = prop_meta.get("name", discriminator._rest_name) + xml_ns = prop_meta.get("ns", model_meta.get("ns", None)) + if xml_ns: + xml_name = "{" + xml_ns + "}" + xml_name + + if data.get(xml_name) is not None: + discriminator_value = data.get(xml_name) + else: + discriminator_value = data.find(xml_name).text # pyright: ignore + else: + discriminator_value = data.get(discriminator._rest_name) + mapped_cls = cls.__mapping__.get(discriminator_value, cls) # pyright: ignore # pylint: disable=no-member + return mapped_cls._deserialize(data, exist_discriminators) + + def as_dict(self, *, exclude_readonly: bool = False) -> dict[str, typing.Any]: + """Return a dict that can be turned into json using json.dump. + + :keyword bool exclude_readonly: Whether to remove the readonly properties. + :returns: A dict JSON compatible object + :rtype: dict + """ + + result = {} + readonly_props = [] + if exclude_readonly: + readonly_props = [p._rest_name for p in self._attr_to_rest_field.values() if _is_readonly(p)] + for k, v in self.items(): + if exclude_readonly and k in readonly_props: # pyright: ignore + continue + is_multipart_file_input = False + try: + is_multipart_file_input = next( + rf for rf in self._attr_to_rest_field.values() if rf._rest_name == k + )._is_multipart_file_input + except StopIteration: + pass + result[k] = v if is_multipart_file_input else Model._as_dict_value(v, exclude_readonly=exclude_readonly) + return result + + @staticmethod + def _as_dict_value(v: typing.Any, exclude_readonly: bool = False) -> typing.Any: + if v is None or isinstance(v, _Null): + return None + if isinstance(v, (list, tuple, set)): + return type(v)(Model._as_dict_value(x, exclude_readonly=exclude_readonly) for x in v) + if isinstance(v, dict): + return {dk: Model._as_dict_value(dv, exclude_readonly=exclude_readonly) for dk, dv in v.items()} + return v.as_dict(exclude_readonly=exclude_readonly) if hasattr(v, "as_dict") else v + + +def _deserialize_model(model_deserializer: typing.Optional[typing.Callable], obj): + if _is_model(obj): + return obj + return _deserialize(model_deserializer, obj) + + +def _deserialize_with_optional(if_obj_deserializer: typing.Optional[typing.Callable], obj): + if obj is None: + return obj + return _deserialize_with_callable(if_obj_deserializer, obj) + + +def _deserialize_with_union(deserializers, obj): + for deserializer in deserializers: + try: + return _deserialize(deserializer, obj) + except DeserializationError: + pass + raise DeserializationError() + + +def _deserialize_dict( + value_deserializer: typing.Optional[typing.Callable], + module: typing.Optional[str], + obj: dict[typing.Any, typing.Any], +): + if obj is None: + return obj + if isinstance(obj, ET.Element): + obj = {child.tag: child for child in obj} + return {k: _deserialize(value_deserializer, v, module) for k, v in obj.items()} + + +def _deserialize_multiple_sequence( + entry_deserializers: list[typing.Optional[typing.Callable]], + module: typing.Optional[str], + obj, +): + if obj is None: + return obj + return type(obj)(_deserialize(deserializer, entry, module) for entry, deserializer in zip(obj, entry_deserializers)) + + +def _deserialize_sequence( + deserializer: typing.Optional[typing.Callable], + module: typing.Optional[str], + obj, +): + if obj is None: + return obj + if isinstance(obj, ET.Element): + obj = list(obj) + return type(obj)(_deserialize(deserializer, entry, module) for entry in obj) + + +def _sorted_annotations(types: list[typing.Any]) -> list[typing.Any]: + return sorted( + types, + key=lambda x: hasattr(x, "__name__") and x.__name__.lower() in ("str", "float", "int", "bool"), + ) + + +def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-return-statements, too-many-statements, too-many-branches + annotation: typing.Any, + module: typing.Optional[str], + rf: typing.Optional["_RestField"] = None, +) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]: + if not annotation: + return None + + # is it a type alias? + if isinstance(annotation, str): + if module is not None: + annotation = _get_type_alias_type(module, annotation) + + # is it a forward ref / in quotes? + if isinstance(annotation, (str, typing.ForwardRef)): + try: + model_name = annotation.__forward_arg__ # type: ignore + except AttributeError: + model_name = annotation + if module is not None: + annotation = _get_model(module, model_name) # type: ignore + + try: + if module and _is_model(annotation): + if rf: + rf._is_model = True + + return functools.partial(_deserialize_model, annotation) # pyright: ignore + except Exception: + pass + + # is it a literal? + try: + if annotation.__origin__ is typing.Literal: # pyright: ignore + return None + except AttributeError: + pass + + # is it optional? + try: + if any(a for a in annotation.__args__ if a == type(None)): # pyright: ignore + if len(annotation.__args__) <= 2: # pyright: ignore + if_obj_deserializer = _get_deserialize_callable_from_annotation( + next(a for a in annotation.__args__ if a != type(None)), module, rf # pyright: ignore + ) + + return functools.partial(_deserialize_with_optional, if_obj_deserializer) + # the type is Optional[Union[...]], we need to remove the None type from the Union + annotation_copy = copy.copy(annotation) + annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a != type(None)] # pyright: ignore + return _get_deserialize_callable_from_annotation(annotation_copy, module, rf) + except AttributeError: + pass + + # is it union? + if getattr(annotation, "__origin__", None) is typing.Union: + # initial ordering is we make `string` the last deserialization option, because it is often them most generic + deserializers = [ + _get_deserialize_callable_from_annotation(arg, module, rf) + for arg in _sorted_annotations(annotation.__args__) # pyright: ignore + ] + + return functools.partial(_deserialize_with_union, deserializers) + + try: + annotation_name = ( + annotation.__name__ if hasattr(annotation, "__name__") else annotation._name # pyright: ignore + ) + if annotation_name.lower() == "dict": + value_deserializer = _get_deserialize_callable_from_annotation( + annotation.__args__[1], module, rf # pyright: ignore + ) + + return functools.partial( + _deserialize_dict, + value_deserializer, + module, + ) + except (AttributeError, IndexError): + pass + try: + annotation_name = ( + annotation.__name__ if hasattr(annotation, "__name__") else annotation._name # pyright: ignore + ) + if annotation_name.lower() in ["list", "set", "tuple", "sequence"]: + if len(annotation.__args__) > 1: # pyright: ignore + entry_deserializers = [ + _get_deserialize_callable_from_annotation(dt, module, rf) + for dt in annotation.__args__ # pyright: ignore + ] + return functools.partial(_deserialize_multiple_sequence, entry_deserializers, module) + deserializer = _get_deserialize_callable_from_annotation( + annotation.__args__[0], module, rf # pyright: ignore + ) + + return functools.partial(_deserialize_sequence, deserializer, module) + except (TypeError, IndexError, AttributeError, SyntaxError): + pass + + def _deserialize_default( + deserializer, + obj, + ): + if obj is None: + return obj + try: + return _deserialize_with_callable(deserializer, obj) + except Exception: + pass + return obj + + if get_deserializer(annotation, rf): + return functools.partial(_deserialize_default, get_deserializer(annotation, rf)) + + return functools.partial(_deserialize_default, annotation) + + +def _deserialize_with_callable( + deserializer: typing.Optional[typing.Callable[[typing.Any], typing.Any]], + value: typing.Any, +): # pylint: disable=too-many-return-statements + try: + if value is None or isinstance(value, _Null): + return None + if isinstance(value, ET.Element): + if deserializer is str: + return value.text or "" + if deserializer is int: + return int(value.text) if value.text else None + if deserializer is float: + return float(value.text) if value.text else None + if deserializer is bool: + return value.text == "true" if value.text else None + if deserializer is None: + return value + if deserializer in [int, float, bool]: + return deserializer(value) + if isinstance(deserializer, CaseInsensitiveEnumMeta): + try: + return deserializer(value) + except ValueError: + # for unknown value, return raw value + return value + if isinstance(deserializer, type) and issubclass(deserializer, Model): + return deserializer._deserialize(value, []) + return typing.cast(typing.Callable[[typing.Any], typing.Any], deserializer)(value) + except Exception as e: + raise DeserializationError() from e + + +def _deserialize( + deserializer: typing.Any, + value: typing.Any, + module: typing.Optional[str] = None, + rf: typing.Optional["_RestField"] = None, + format: typing.Optional[str] = None, +) -> typing.Any: + if isinstance(value, PipelineResponse): + value = value.http_response.json() + if rf is None and format: + rf = _RestField(format=format) + if not isinstance(deserializer, functools.partial): + deserializer = _get_deserialize_callable_from_annotation(deserializer, module, rf) + return _deserialize_with_callable(deserializer, value) + + +def _failsafe_deserialize( + deserializer: typing.Any, + response: HttpResponse, + module: typing.Optional[str] = None, + rf: typing.Optional["_RestField"] = None, + format: typing.Optional[str] = None, +) -> typing.Any: + try: + return _deserialize(deserializer, response.json(), module, rf, format) + except DeserializationError: + _LOGGER.warning( + "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True + ) + return None + + +def _failsafe_deserialize_xml( + deserializer: typing.Any, + response: HttpResponse, +) -> typing.Any: + try: + return _deserialize_xml(deserializer, response.text()) + except DeserializationError: + _LOGGER.warning( + "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True + ) + return None + + +class _RestField: + def __init__( + self, + *, + name: typing.Optional[str] = None, + type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin + is_discriminator: bool = False, + visibility: typing.Optional[list[str]] = None, + default: typing.Any = _UNSET, + format: typing.Optional[str] = None, + is_multipart_file_input: bool = False, + xml: typing.Optional[dict[str, typing.Any]] = None, + ): + self._type = type + self._rest_name_input = name + self._module: typing.Optional[str] = None + self._is_discriminator = is_discriminator + self._visibility = visibility + self._is_model = False + self._default = default + self._format = format + self._is_multipart_file_input = is_multipart_file_input + self._xml = xml if xml is not None else {} + + @property + def _class_type(self) -> typing.Any: + return getattr(self._type, "args", [None])[0] + + @property + def _rest_name(self) -> str: + if self._rest_name_input is None: + raise ValueError("Rest name was never set") + return self._rest_name_input + + def __get__(self, obj: Model, type=None): # pylint: disable=redefined-builtin + # by this point, type and rest_name will have a value bc we default + # them in __new__ of the Model class + item = obj.get(self._rest_name) + if item is None: + return item + if self._is_model: + return item + return _deserialize(self._type, _serialize(item, self._format), rf=self) + + def __set__(self, obj: Model, value) -> None: + if value is None: + # we want to wipe out entries if users set attr to None + try: + obj.__delitem__(self._rest_name) + except KeyError: + pass + return + if self._is_model: + if not _is_model(value): + value = _deserialize(self._type, value) + obj.__setitem__(self._rest_name, value) + return + obj.__setitem__(self._rest_name, _serialize(value, self._format)) + + def _get_deserialize_callable_from_annotation( + self, annotation: typing.Any + ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]: + return _get_deserialize_callable_from_annotation(annotation, self._module, self) + + +def rest_field( + *, + name: typing.Optional[str] = None, + type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin + visibility: typing.Optional[list[str]] = None, + default: typing.Any = _UNSET, + format: typing.Optional[str] = None, + is_multipart_file_input: bool = False, + xml: typing.Optional[dict[str, typing.Any]] = None, +) -> typing.Any: + return _RestField( + name=name, + type=type, + visibility=visibility, + default=default, + format=format, + is_multipart_file_input=is_multipart_file_input, + xml=xml, + ) + + +def rest_discriminator( + *, + name: typing.Optional[str] = None, + type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin + visibility: typing.Optional[list[str]] = None, + xml: typing.Optional[dict[str, typing.Any]] = None, +) -> typing.Any: + return _RestField(name=name, type=type, is_discriminator=True, visibility=visibility, xml=xml) + + +def serialize_xml(model: Model, exclude_readonly: bool = False) -> str: + """Serialize a model to XML. + + :param Model model: The model to serialize. + :param bool exclude_readonly: Whether to exclude readonly properties. + :returns: The XML representation of the model. + :rtype: str + """ + return ET.tostring(_get_element(model, exclude_readonly), encoding="unicode") # type: ignore + + +def _get_element( + o: typing.Any, + exclude_readonly: bool = False, + parent_meta: typing.Optional[dict[str, typing.Any]] = None, + wrapped_element: typing.Optional[ET.Element] = None, +) -> typing.Union[ET.Element, list[ET.Element]]: + if _is_model(o): + model_meta = getattr(o, "_xml", {}) + + # if prop is a model, then use the prop element directly, else generate a wrapper of model + if wrapped_element is None: + wrapped_element = _create_xml_element( + model_meta.get("name", o.__class__.__name__), + model_meta.get("prefix"), + model_meta.get("ns"), + ) + + readonly_props = [] + if exclude_readonly: + readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)] + + for k, v in o.items(): + # do not serialize readonly properties + if exclude_readonly and k in readonly_props: + continue + + prop_rest_field = _get_rest_field(o._attr_to_rest_field, k) + if prop_rest_field: + prop_meta = getattr(prop_rest_field, "_xml").copy() + # use the wire name as xml name if no specific name is set + if prop_meta.get("name") is None: + prop_meta["name"] = k + else: + # additional properties will not have rest field, use the wire name as xml name + prop_meta = {"name": k} + + # if no ns for prop, use model's + if prop_meta.get("ns") is None and model_meta.get("ns"): + prop_meta["ns"] = model_meta.get("ns") + prop_meta["prefix"] = model_meta.get("prefix") + + if prop_meta.get("unwrapped", False): + # unwrapped could only set on array + wrapped_element.extend(_get_element(v, exclude_readonly, prop_meta)) + elif prop_meta.get("text", False): + # text could only set on primitive type + wrapped_element.text = _get_primitive_type_value(v) + elif prop_meta.get("attribute", False): + xml_name = prop_meta.get("name", k) + if prop_meta.get("ns"): + ET.register_namespace(prop_meta.get("prefix"), prop_meta.get("ns")) # pyright: ignore + xml_name = "{" + prop_meta.get("ns") + "}" + xml_name # pyright: ignore + # attribute should be primitive type + wrapped_element.set(xml_name, _get_primitive_type_value(v)) + else: + # other wrapped prop element + wrapped_element.append(_get_wrapped_element(v, exclude_readonly, prop_meta)) + return wrapped_element + if isinstance(o, list): + return [_get_element(x, exclude_readonly, parent_meta) for x in o] # type: ignore + if isinstance(o, dict): + result = [] + for k, v in o.items(): + result.append( + _get_wrapped_element( + v, + exclude_readonly, + { + "name": k, + "ns": parent_meta.get("ns") if parent_meta else None, + "prefix": parent_meta.get("prefix") if parent_meta else None, + }, + ) + ) + return result + + # primitive case need to create element based on parent_meta + if parent_meta: + return _get_wrapped_element( + o, + exclude_readonly, + { + "name": parent_meta.get("itemsName", parent_meta.get("name")), + "prefix": parent_meta.get("itemsPrefix", parent_meta.get("prefix")), + "ns": parent_meta.get("itemsNs", parent_meta.get("ns")), + }, + ) + + raise ValueError("Could not serialize value into xml: " + o) + + +def _get_wrapped_element( + v: typing.Any, + exclude_readonly: bool, + meta: typing.Optional[dict[str, typing.Any]], +) -> ET.Element: + wrapped_element = _create_xml_element( + meta.get("name") if meta else None, meta.get("prefix") if meta else None, meta.get("ns") if meta else None + ) + if isinstance(v, (dict, list)): + wrapped_element.extend(_get_element(v, exclude_readonly, meta)) + elif _is_model(v): + _get_element(v, exclude_readonly, meta, wrapped_element) + else: + wrapped_element.text = _get_primitive_type_value(v) + return wrapped_element + + +def _get_primitive_type_value(v) -> str: + if v is True: + return "true" + if v is False: + return "false" + if isinstance(v, _Null): + return "" + return str(v) + + +def _create_xml_element(tag, prefix=None, ns=None): + if prefix and ns: + ET.register_namespace(prefix, ns) + if ns: + return ET.Element("{" + ns + "}" + tag) + return ET.Element(tag) + + +def _deserialize_xml( + deserializer: typing.Any, + value: str, +) -> typing.Any: + element = ET.fromstring(value) # nosec + return _deserialize(deserializer, element) + + +def _convert_element(e: ET.Element): + # dict case + if len(e.attrib) > 0 or len({child.tag for child in e}) > 1: + dict_result: dict[str, typing.Any] = {} + for child in e: + if dict_result.get(child.tag) is not None: + if isinstance(dict_result[child.tag], list): + dict_result[child.tag].append(_convert_element(child)) + else: + dict_result[child.tag] = [dict_result[child.tag], _convert_element(child)] + else: + dict_result[child.tag] = _convert_element(child) + dict_result.update(e.attrib) + return dict_result + # array case + if len(e) > 0: + array_result: list[typing.Any] = [] + for child in e: + array_result.append(_convert_element(child)) + return array_result + # primitive case + return e.text diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_serialization.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/serialization.py similarity index 82% rename from sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_serialization.py rename to sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/serialization.py index 2f781d740827..e81921cbb011 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_serialization.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/serialization.py @@ -1,30 +1,12 @@ +# pylint: disable=line-too-long,useless-suppression,too-many-lines +# coding=utf-8 # -------------------------------------------------------------------------- -# # Copyright (c) Microsoft Corporation. All rights reserved. -# -# The MIT License (MIT) -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the ""Software""), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. -# +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -# pylint: skip-file # pyright: reportUnnecessaryTypeIgnoreComment=false from base64 import b64decode, b64encode @@ -39,7 +21,6 @@ import sys import codecs from typing import ( - Dict, Any, cast, Optional, @@ -48,11 +29,7 @@ IO, Mapping, Callable, - TypeVar, MutableMapping, - Type, - List, - Mapping, ) try: @@ -62,13 +39,13 @@ import xml.etree.ElementTree as ET import isodate # type: ignore +from typing_extensions import Self from azure.core.exceptions import DeserializationError, SerializationError from azure.core.serialization import NULL as CoreNull _BOM = codecs.BOM_UTF8.decode(encoding="utf-8") -ModelType = TypeVar("ModelType", bound="Model") JSON = MutableMapping[str, Any] @@ -91,6 +68,8 @@ def deserialize_from_text(cls, data: Optional[Union[AnyStr, IO]], content_type: :param data: Input, could be bytes or stream (will be decoded with UTF8) or text :type data: str or bytes or IO :param str content_type: The content type. + :return: The deserialized data. + :rtype: object """ if hasattr(data, "read"): # Assume a stream @@ -112,7 +91,7 @@ def deserialize_from_text(cls, data: Optional[Union[AnyStr, IO]], content_type: try: return json.loads(data_as_str) except ValueError as err: - raise DeserializationError("JSON is invalid: {}".format(err), err) + raise DeserializationError("JSON is invalid: {}".format(err), err) from err elif "xml" in (content_type or []): try: @@ -144,6 +123,8 @@ def _json_attemp(data): # context otherwise. _LOGGER.critical("Wasn't XML not JSON, failing") raise DeserializationError("XML is invalid") from err + elif content_type.startswith("text/"): + return data_as_str raise DeserializationError("Cannot deserialize content-type: {}".format(content_type)) @classmethod @@ -153,6 +134,11 @@ def deserialize_from_http_generics(cls, body_bytes: Optional[Union[AnyStr, IO]], Use bytes and headers to NOT use any requests/aiohttp or whatever specific implementation. Headers will tested for "content-type" + + :param bytes body_bytes: The body of the response. + :param dict headers: The headers of the response. + :returns: The deserialized data. + :rtype: object """ # Try to use content-type from headers if available content_type = None @@ -177,80 +163,31 @@ def deserialize_from_http_generics(cls, body_bytes: Optional[Union[AnyStr, IO]], except NameError: _long_type = int - -class UTC(datetime.tzinfo): - """Time Zone info for handling UTC""" - - def utcoffset(self, dt): - """UTF offset for UTC is 0.""" - return datetime.timedelta(0) - - def tzname(self, dt): - """Timestamp representation.""" - return "Z" - - def dst(self, dt): - """No daylight saving for UTC.""" - return datetime.timedelta(hours=1) - - -try: - from datetime import timezone as _FixedOffset # type: ignore -except ImportError: # Python 2.7 - - class _FixedOffset(datetime.tzinfo): # type: ignore - """Fixed offset in minutes east from UTC. - Copy/pasted from Python doc - :param datetime.timedelta offset: offset in timedelta format - """ - - def __init__(self, offset): - self.__offset = offset - - def utcoffset(self, dt): - return self.__offset - - def tzname(self, dt): - return str(self.__offset.total_seconds() / 3600) - - def __repr__(self): - return "".format(self.tzname(None)) - - def dst(self, dt): - return datetime.timedelta(0) - - def __getinitargs__(self): - return (self.__offset,) - - -try: - from datetime import timezone - - TZ_UTC = timezone.utc -except ImportError: - TZ_UTC = UTC() # type: ignore +TZ_UTC = datetime.timezone.utc _FLATTEN = re.compile(r"(? None: - self.additional_properties: Optional[Dict[str, Any]] = {} - for k in kwargs: + self.additional_properties: Optional[dict[str, Any]] = {} + for k in kwargs: # pylint: disable=consider-using-dict-items if k not in self._attribute_map: _LOGGER.warning("%s is not a known attribute of class %s and will be ignored", k, self.__class__) elif k in self._validation and self._validation[k].get("readonly", False): @@ -298,13 +242,23 @@ def __init__(self, **kwargs: Any) -> None: setattr(self, k, kwargs[k]) def __eq__(self, other: Any) -> bool: - """Compare objects by comparing all attributes.""" + """Compare objects by comparing all attributes. + + :param object other: The object to compare + :returns: True if objects are equal + :rtype: bool + """ if isinstance(other, self.__class__): return self.__dict__ == other.__dict__ return False def __ne__(self, other: Any) -> bool: - """Compare objects by comparing all attributes.""" + """Compare objects by comparing all attributes. + + :param object other: The object to compare + :returns: True if objects are not equal + :rtype: bool + """ return not self.__eq__(other) def __str__(self) -> str: @@ -324,7 +278,11 @@ def is_xml_model(cls) -> bool: @classmethod def _create_xml_node(cls): - """Create XML node.""" + """Create XML node. + + :returns: The XML node + :rtype: xml.etree.ElementTree.Element + """ try: xml_map = cls._xml_map # type: ignore except AttributeError: @@ -344,12 +302,14 @@ def serialize(self, keep_readonly: bool = False, **kwargs: Any) -> JSON: :rtype: dict """ serializer = Serializer(self._infer_class_models()) - return serializer._serialize(self, keep_readonly=keep_readonly, **kwargs) # type: ignore + return serializer._serialize( # type: ignore # pylint: disable=protected-access + self, keep_readonly=keep_readonly, **kwargs + ) def as_dict( self, keep_readonly: bool = True, - key_transformer: Callable[[str, Dict[str, Any], Any], Any] = attribute_transformer, + key_transformer: Callable[[str, dict[str, Any], Any], Any] = attribute_transformer, **kwargs: Any ) -> JSON: """Return a dict that can be serialized using json.dump. @@ -378,12 +338,15 @@ def my_key_transformer(key, attr_desc, value): If you want XML serialization, you can pass the kwargs is_xml=True. + :param bool keep_readonly: If you want to serialize the readonly attributes :param function key_transformer: A key transformer function. :returns: A dict JSON compatible object :rtype: dict """ serializer = Serializer(self._infer_class_models()) - return serializer._serialize(self, key_transformer=key_transformer, keep_readonly=keep_readonly, **kwargs) # type: ignore + return serializer._serialize( # type: ignore # pylint: disable=protected-access + self, key_transformer=key_transformer, keep_readonly=keep_readonly, **kwargs + ) @classmethod def _infer_class_models(cls): @@ -393,30 +356,31 @@ def _infer_class_models(cls): client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} if cls.__name__ not in client_models: raise ValueError("Not Autorest generated code") - except Exception: + except Exception: # pylint: disable=broad-exception-caught # Assume it's not Autorest generated (tests?). Add ourselves as dependencies. client_models = {cls.__name__: cls} return client_models @classmethod - def deserialize(cls: Type[ModelType], data: Any, content_type: Optional[str] = None) -> ModelType: + def deserialize(cls, data: Any, content_type: Optional[str] = None) -> Self: """Parse a str using the RestAPI syntax and return a model. :param str data: A str using RestAPI structure. JSON by default. :param str content_type: JSON by default, set application/xml if XML. :returns: An instance of this model - :raises: DeserializationError if something went wrong + :raises DeserializationError: if something went wrong + :rtype: Self """ deserializer = Deserializer(cls._infer_class_models()) return deserializer(cls.__name__, data, content_type=content_type) # type: ignore @classmethod def from_dict( - cls: Type[ModelType], + cls, data: Any, - key_extractors: Optional[Callable[[str, Dict[str, Any], Any], Any]] = None, + key_extractors: Optional[Callable[[str, dict[str, Any], Any], Any]] = None, content_type: Optional[str] = None, - ) -> ModelType: + ) -> Self: """Parse a dict using given key extractor return a model. By default consider key @@ -424,9 +388,11 @@ def from_dict( and last_rest_key_case_insensitive_extractor) :param dict data: A dict using RestAPI structure + :param function key_extractors: A key extractor function. :param str content_type: JSON by default, set application/xml if XML. :returns: An instance of this model - :raises: DeserializationError if something went wrong + :raises DeserializationError: if something went wrong + :rtype: Self """ deserializer = Deserializer(cls._infer_class_models()) deserializer.key_extractors = ( # type: ignore @@ -446,21 +412,25 @@ def _flatten_subtype(cls, key, objects): return {} result = dict(cls._subtype_map[key]) for valuetype in cls._subtype_map[key].values(): - result.update(objects[valuetype]._flatten_subtype(key, objects)) + result |= objects[valuetype]._flatten_subtype(key, objects) # pylint: disable=protected-access return result @classmethod def _classify(cls, response, objects): """Check the class _subtype_map for any child classes. We want to ignore any inherited _subtype_maps. - Remove the polymorphic key from the initial data. + + :param dict response: The initial data + :param dict objects: The class objects + :returns: The class to be used + :rtype: class """ for subtype_key in cls.__dict__.get("_subtype_map", {}).keys(): subtype_value = None if not isinstance(response, ET.Element): rest_api_response_key = cls._get_rest_key_parts(subtype_key)[-1] - subtype_value = response.pop(rest_api_response_key, None) or response.pop(subtype_key, None) + subtype_value = response.get(rest_api_response_key, None) or response.get(subtype_key, None) else: subtype_value = xml_key_extractor(subtype_key, cls._attribute_map[subtype_key], response) if subtype_value: @@ -499,11 +469,13 @@ def _decode_attribute_map_key(key): inside the received data. :param str key: A key string from the generated code + :returns: The decoded key + :rtype: str """ return key.replace("\\.", ".") -class Serializer(object): +class Serializer: # pylint: disable=too-many-public-methods """Request object model serializer.""" basic_types = {str: "str", int: "int", bool: "bool", float: "float"} @@ -538,7 +510,7 @@ class Serializer(object): "multiple": lambda x, y: x % y != 0, } - def __init__(self, classes: Optional[Mapping[str, type]] = None): + def __init__(self, classes: Optional[Mapping[str, type]] = None) -> None: self.serialize_type = { "iso-8601": Serializer.serialize_iso, "rfc-1123": Serializer.serialize_rfc, @@ -554,17 +526,20 @@ def __init__(self, classes: Optional[Mapping[str, type]] = None): "[]": self.serialize_iter, "{}": self.serialize_dict, } - self.dependencies: Dict[str, type] = dict(classes) if classes else {} + self.dependencies: dict[str, type] = dict(classes) if classes else {} self.key_transformer = full_restapi_key_transformer self.client_side_validation = True - def _serialize(self, target_obj, data_type=None, **kwargs): + def _serialize( # pylint: disable=too-many-nested-blocks, too-many-branches, too-many-statements, too-many-locals + self, target_obj, data_type=None, **kwargs + ): """Serialize data into a string according to type. - :param target_obj: The data to be serialized. + :param object target_obj: The data to be serialized. :param str data_type: The type to be serialized from. :rtype: str, dict - :raises: SerializationError if serialization fails. + :raises SerializationError: if serialization fails. + :returns: The serialized data. """ key_transformer = kwargs.get("key_transformer", self.key_transformer) keep_readonly = kwargs.get("keep_readonly", False) @@ -590,17 +565,19 @@ def _serialize(self, target_obj, data_type=None, **kwargs): serialized = {} if is_xml_model_serialization: - serialized = target_obj._create_xml_node() + serialized = target_obj._create_xml_node() # pylint: disable=protected-access try: - attributes = target_obj._attribute_map + attributes = target_obj._attribute_map # pylint: disable=protected-access for attr, attr_desc in attributes.items(): attr_name = attr - if not keep_readonly and target_obj._validation.get(attr_name, {}).get("readonly", False): + if not keep_readonly and target_obj._validation.get( # pylint: disable=protected-access + attr_name, {} + ).get("readonly", False): continue if attr_name == "additional_properties" and attr_desc["key"] == "": if target_obj.additional_properties is not None: - serialized.update(target_obj.additional_properties) + serialized |= target_obj.additional_properties continue try: @@ -631,7 +608,8 @@ def _serialize(self, target_obj, data_type=None, **kwargs): if isinstance(new_attr, list): serialized.extend(new_attr) # type: ignore elif isinstance(new_attr, ET.Element): - # If the down XML has no XML/Name, we MUST replace the tag with the local tag. But keeping the namespaces. + # If the down XML has no XML/Name, + # we MUST replace the tag with the local tag. But keeping the namespaces. if "name" not in getattr(orig_attr, "_xml_map", {}): splitted_tag = new_attr.tag.split("}") if len(splitted_tag) == 2: # Namespace @@ -662,17 +640,17 @@ def _serialize(self, target_obj, data_type=None, **kwargs): except (AttributeError, KeyError, TypeError) as err: msg = "Attribute {} in object {} cannot be serialized.\n{}".format(attr_name, class_name, str(target_obj)) raise SerializationError(msg) from err - else: - return serialized + return serialized def body(self, data, data_type, **kwargs): """Serialize data intended for a request body. - :param data: The data to be serialized. + :param object data: The data to be serialized. :param str data_type: The type to be serialized from. :rtype: dict - :raises: SerializationError if serialization fails. - :raises: ValueError if data is None + :raises SerializationError: if serialization fails. + :raises ValueError: if data is None + :returns: The serialized request body """ # Just in case this is a dict @@ -701,7 +679,7 @@ def body(self, data, data_type, **kwargs): attribute_key_case_insensitive_extractor, last_rest_key_case_insensitive_extractor, ] - data = deserializer._deserialize(data_type, data) + data = deserializer._deserialize(data_type, data) # pylint: disable=protected-access except DeserializationError as err: raise SerializationError("Unable to build a model: " + str(err)) from err @@ -710,11 +688,13 @@ def body(self, data, data_type, **kwargs): def url(self, name, data, data_type, **kwargs): """Serialize data intended for a URL path. - :param data: The data to be serialized. + :param str name: The name of the URL path parameter. + :param object data: The data to be serialized. :param str data_type: The type to be serialized from. :rtype: str - :raises: TypeError if serialization fails. - :raises: ValueError if data is None + :returns: The serialized URL path + :raises TypeError: if serialization fails. + :raises ValueError: if data is None """ try: output = self.serialize_data(data, data_type, **kwargs) @@ -726,21 +706,20 @@ def url(self, name, data, data_type, **kwargs): output = output.replace("{", quote("{")).replace("}", quote("}")) else: output = quote(str(output), safe="") - except SerializationError: - raise TypeError("{} must be type {}.".format(name, data_type)) - else: - return output + except SerializationError as exc: + raise TypeError("{} must be type {}.".format(name, data_type)) from exc + return output def query(self, name, data, data_type, **kwargs): """Serialize data intended for a URL query. - :param data: The data to be serialized. + :param str name: The name of the query parameter. + :param object data: The data to be serialized. :param str data_type: The type to be serialized from. - :keyword bool skip_quote: Whether to skip quote the serialized result. - Defaults to False. :rtype: str, list - :raises: TypeError if serialization fails. - :raises: ValueError if data is None + :raises TypeError: if serialization fails. + :raises ValueError: if data is None + :returns: The serialized query parameter """ try: # Treat the list aside, since we don't want to encode the div separator @@ -757,19 +736,20 @@ def query(self, name, data, data_type, **kwargs): output = str(output) else: output = quote(str(output), safe="") - except SerializationError: - raise TypeError("{} must be type {}.".format(name, data_type)) - else: - return str(output) + except SerializationError as exc: + raise TypeError("{} must be type {}.".format(name, data_type)) from exc + return str(output) def header(self, name, data, data_type, **kwargs): """Serialize data intended for a request header. - :param data: The data to be serialized. + :param str name: The name of the header. + :param object data: The data to be serialized. :param str data_type: The type to be serialized from. :rtype: str - :raises: TypeError if serialization fails. - :raises: ValueError if data is None + :raises TypeError: if serialization fails. + :raises ValueError: if data is None + :returns: The serialized header """ try: if data_type in ["[str]"]: @@ -778,21 +758,20 @@ def header(self, name, data, data_type, **kwargs): output = self.serialize_data(data, data_type, **kwargs) if data_type == "bool": output = json.dumps(output) - except SerializationError: - raise TypeError("{} must be type {}.".format(name, data_type)) - else: - return str(output) + except SerializationError as exc: + raise TypeError("{} must be type {}.".format(name, data_type)) from exc + return str(output) def serialize_data(self, data, data_type, **kwargs): """Serialize generic data according to supplied data type. - :param data: The data to be serialized. + :param object data: The data to be serialized. :param str data_type: The type to be serialized from. - :param bool required: Whether it's essential that the data not be - empty or None - :raises: AttributeError if required data is None. - :raises: ValueError if data is None - :raises: SerializationError if serialization fails. + :raises AttributeError: if required data is None. + :raises ValueError: if data is None + :raises SerializationError: if serialization fails. + :returns: The serialized data. + :rtype: str, int, float, bool, dict, list """ if data is None: raise ValueError("No value for given attribute") @@ -803,7 +782,7 @@ def serialize_data(self, data, data_type, **kwargs): if data_type in self.basic_types.values(): return self.serialize_basic(data, data_type, **kwargs) - elif data_type in self.serialize_type: + if data_type in self.serialize_type: return self.serialize_type[data_type](data, **kwargs) # If dependencies is empty, try with current data class @@ -819,11 +798,10 @@ def serialize_data(self, data, data_type, **kwargs): except (ValueError, TypeError) as err: msg = "Unable to serialize value: {!r} as type: {!r}." raise SerializationError(msg.format(data, data_type)) from err - else: - return self._serialize(data, **kwargs) + return self._serialize(data, **kwargs) @classmethod - def _get_custom_serializers(cls, data_type, **kwargs): + def _get_custom_serializers(cls, data_type, **kwargs): # pylint: disable=inconsistent-return-statements custom_serializer = kwargs.get("basic_types_serializers", {}).get(data_type) if custom_serializer: return custom_serializer @@ -839,23 +817,26 @@ def serialize_basic(cls, data, data_type, **kwargs): - basic_types_serializers dict[str, callable] : If set, use the callable as serializer - is_xml bool : If set, use xml_basic_types_serializers - :param data: Object to be serialized. + :param obj data: Object to be serialized. :param str data_type: Type of object in the iterable. + :rtype: str, int, float, bool + :return: serialized object """ custom_serializer = cls._get_custom_serializers(data_type, **kwargs) if custom_serializer: return custom_serializer(data) if data_type == "str": return cls.serialize_unicode(data) - return eval(data_type)(data) # nosec + return eval(data_type)(data) # nosec # pylint: disable=eval-used @classmethod def serialize_unicode(cls, data): """Special handling for serializing unicode strings in Py2. Encode to UTF-8 if unicode, otherwise handle as a str. - :param data: Object to be serialized. + :param str data: Object to be serialized. :rtype: str + :return: serialized object """ try: # If I received an enum, return its value return data.value @@ -869,8 +850,7 @@ def serialize_unicode(cls, data): return data except NameError: return str(data) - else: - return str(data) + return str(data) def serialize_iter(self, data, iter_type, div=None, **kwargs): """Serialize iterable. @@ -880,15 +860,13 @@ def serialize_iter(self, data, iter_type, div=None, **kwargs): serialization_ctxt['type'] should be same as data_type. - is_xml bool : If set, serialize as XML - :param list attr: Object to be serialized. + :param list data: Object to be serialized. :param str iter_type: Type of object in the iterable. - :param bool required: Whether the objects in the iterable must - not be None or empty. :param str div: If set, this str will be used to combine the elements in the iterable into a combined string. Default is 'None'. - :keyword bool do_quote: Whether to quote the serialized result of each iterable element. Defaults to False. :rtype: list, str + :return: serialized iterable """ if isinstance(data, str): raise SerializationError("Refuse str type as a valid iter type.") @@ -943,9 +921,8 @@ def serialize_dict(self, attr, dict_type, **kwargs): :param dict attr: Object to be serialized. :param str dict_type: Type of object in the dictionary. - :param bool required: Whether the objects in the dictionary must - not be None or empty. :rtype: dict + :return: serialized dictionary """ serialization_ctxt = kwargs.get("serialization_ctxt", {}) serialized = {} @@ -969,7 +946,7 @@ def serialize_dict(self, attr, dict_type, **kwargs): return serialized - def serialize_object(self, attr, **kwargs): + def serialize_object(self, attr, **kwargs): # pylint: disable=too-many-return-statements """Serialize a generic object. This will be handled as a dictionary. If object passed in is not a basic type (str, int, float, dict, list) it will simply be @@ -977,6 +954,7 @@ def serialize_object(self, attr, **kwargs): :param dict attr: Object to be serialized. :rtype: dict or str + :return: serialized object """ if attr is None: return None @@ -1001,7 +979,7 @@ def serialize_object(self, attr, **kwargs): return self.serialize_decimal(attr) # If it's a model or I know this dependency, serialize as a Model - elif obj_type in self.dependencies.values() or isinstance(attr, Model): + if obj_type in self.dependencies.values() or isinstance(attr, Model): return self._serialize(attr) if obj_type == dict: @@ -1032,56 +1010,61 @@ def serialize_enum(attr, enum_obj=None): try: enum_obj(result) # type: ignore return result - except ValueError: + except ValueError as exc: for enum_value in enum_obj: # type: ignore if enum_value.value.lower() == str(attr).lower(): return enum_value.value error = "{!r} is not valid value for enum {!r}" - raise SerializationError(error.format(attr, enum_obj)) + raise SerializationError(error.format(attr, enum_obj)) from exc @staticmethod - def serialize_bytearray(attr, **kwargs): + def serialize_bytearray(attr, **kwargs): # pylint: disable=unused-argument """Serialize bytearray into base-64 string. - :param attr: Object to be serialized. + :param str attr: Object to be serialized. :rtype: str + :return: serialized base64 """ return b64encode(attr).decode() @staticmethod - def serialize_base64(attr, **kwargs): + def serialize_base64(attr, **kwargs): # pylint: disable=unused-argument """Serialize str into base-64 string. - :param attr: Object to be serialized. + :param str attr: Object to be serialized. :rtype: str + :return: serialized base64 """ encoded = b64encode(attr).decode("ascii") return encoded.strip("=").replace("+", "-").replace("/", "_") @staticmethod - def serialize_decimal(attr, **kwargs): + def serialize_decimal(attr, **kwargs): # pylint: disable=unused-argument """Serialize Decimal object to float. - :param attr: Object to be serialized. + :param decimal attr: Object to be serialized. :rtype: float + :return: serialized decimal """ return float(attr) @staticmethod - def serialize_long(attr, **kwargs): + def serialize_long(attr, **kwargs): # pylint: disable=unused-argument """Serialize long (Py2) or int (Py3). - :param attr: Object to be serialized. + :param int attr: Object to be serialized. :rtype: int/long + :return: serialized long """ return _long_type(attr) @staticmethod - def serialize_date(attr, **kwargs): + def serialize_date(attr, **kwargs): # pylint: disable=unused-argument """Serialize Date object into ISO-8601 formatted string. :param Date attr: Object to be serialized. :rtype: str + :return: serialized date """ if isinstance(attr, str): attr = isodate.parse_date(attr) @@ -1089,11 +1072,12 @@ def serialize_date(attr, **kwargs): return t @staticmethod - def serialize_time(attr, **kwargs): + def serialize_time(attr, **kwargs): # pylint: disable=unused-argument """Serialize Time object into ISO-8601 formatted string. :param datetime.time attr: Object to be serialized. :rtype: str + :return: serialized time """ if isinstance(attr, str): attr = isodate.parse_time(attr) @@ -1103,30 +1087,32 @@ def serialize_time(attr, **kwargs): return t @staticmethod - def serialize_duration(attr, **kwargs): + def serialize_duration(attr, **kwargs): # pylint: disable=unused-argument """Serialize TimeDelta object into ISO-8601 formatted string. :param TimeDelta attr: Object to be serialized. :rtype: str + :return: serialized duration """ if isinstance(attr, str): attr = isodate.parse_duration(attr) return isodate.duration_isoformat(attr) @staticmethod - def serialize_rfc(attr, **kwargs): + def serialize_rfc(attr, **kwargs): # pylint: disable=unused-argument """Serialize Datetime object into RFC-1123 formatted string. :param Datetime attr: Object to be serialized. :rtype: str - :raises: TypeError if format invalid. + :raises TypeError: if format invalid. + :return: serialized rfc """ try: if not attr.tzinfo: _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") utc = attr.utctimetuple() - except AttributeError: - raise TypeError("RFC1123 object must be valid Datetime object.") + except AttributeError as exc: + raise TypeError("RFC1123 object must be valid Datetime object.") from exc return "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT".format( Serializer.days[utc.tm_wday], @@ -1139,12 +1125,13 @@ def serialize_rfc(attr, **kwargs): ) @staticmethod - def serialize_iso(attr, **kwargs): + def serialize_iso(attr, **kwargs): # pylint: disable=unused-argument """Serialize Datetime object into ISO-8601 formatted string. :param Datetime attr: Object to be serialized. :rtype: str - :raises: SerializationError if format invalid. + :raises SerializationError: if format invalid. + :return: serialized iso """ if isinstance(attr, str): attr = isodate.parse_datetime(attr) @@ -1170,13 +1157,14 @@ def serialize_iso(attr, **kwargs): raise TypeError(msg) from err @staticmethod - def serialize_unix(attr, **kwargs): + def serialize_unix(attr, **kwargs): # pylint: disable=unused-argument """Serialize Datetime object into IntTime format. This is represented as seconds. :param Datetime attr: Object to be serialized. :rtype: int - :raises: SerializationError if format invalid + :raises SerializationError: if format invalid + :return: serialied unix """ if isinstance(attr, int): return attr @@ -1184,17 +1172,17 @@ def serialize_unix(attr, **kwargs): if not attr.tzinfo: _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") return int(calendar.timegm(attr.utctimetuple())) - except AttributeError: - raise TypeError("Unix time object must be valid Datetime object.") + except AttributeError as exc: + raise TypeError("Unix time object must be valid Datetime object.") from exc -def rest_key_extractor(attr, attr_desc, data): +def rest_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument key = attr_desc["key"] working_data = data while "." in key: # Need the cast, as for some reasons "split" is typed as list[str | Any] - dict_keys = cast(List[str], _FLATTEN.split(key)) + dict_keys = cast(list[str], _FLATTEN.split(key)) if len(dict_keys) == 1: key = _decode_attribute_map_key(dict_keys[0]) break @@ -1209,7 +1197,9 @@ def rest_key_extractor(attr, attr_desc, data): return working_data.get(key) -def rest_key_case_insensitive_extractor(attr, attr_desc, data): +def rest_key_case_insensitive_extractor( # pylint: disable=unused-argument, inconsistent-return-statements + attr, attr_desc, data +): key = attr_desc["key"] working_data = data @@ -1230,17 +1220,29 @@ def rest_key_case_insensitive_extractor(attr, attr_desc, data): return attribute_key_case_insensitive_extractor(key, None, working_data) -def last_rest_key_extractor(attr, attr_desc, data): - """Extract the attribute in "data" based on the last part of the JSON path key.""" +def last_rest_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument + """Extract the attribute in "data" based on the last part of the JSON path key. + + :param str attr: The attribute to extract + :param dict attr_desc: The attribute description + :param dict data: The data to extract from + :rtype: object + :returns: The extracted attribute + """ key = attr_desc["key"] dict_keys = _FLATTEN.split(key) return attribute_key_extractor(dict_keys[-1], None, data) -def last_rest_key_case_insensitive_extractor(attr, attr_desc, data): +def last_rest_key_case_insensitive_extractor(attr, attr_desc, data): # pylint: disable=unused-argument """Extract the attribute in "data" based on the last part of the JSON path key. This is the case insensitive version of "last_rest_key_extractor" + :param str attr: The attribute to extract + :param dict attr_desc: The attribute description + :param dict data: The data to extract from + :rtype: object + :returns: The extracted attribute """ key = attr_desc["key"] dict_keys = _FLATTEN.split(key) @@ -1277,7 +1279,7 @@ def _extract_name_from_internal_type(internal_type): return xml_name -def xml_key_extractor(attr, attr_desc, data): +def xml_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument,too-many-return-statements if isinstance(data, dict): return None @@ -1329,22 +1331,21 @@ def xml_key_extractor(attr, attr_desc, data): if is_iter_type: if is_wrapped: return None # is_wrapped no node, we want None - else: - return [] # not wrapped, assume empty list + return [] # not wrapped, assume empty list return None # Assume it's not there, maybe an optional node. # If is_iter_type and not wrapped, return all found children if is_iter_type: if not is_wrapped: return children - else: # Iter and wrapped, should have found one node only (the wrap one) - if len(children) != 1: - raise DeserializationError( - "Tried to deserialize an array not wrapped, and found several nodes '{}'. Maybe you should declare this array as wrapped?".format( - xml_name - ) + # Iter and wrapped, should have found one node only (the wrap one) + if len(children) != 1: + raise DeserializationError( + "Tried to deserialize an array not wrapped, and found several nodes '{}'. Maybe you should declare this array as wrapped?".format( + xml_name ) - return list(children[0]) # Might be empty list and that's ok. + ) + return list(children[0]) # Might be empty list and that's ok. # Here it's not a itertype, we should have found one element only or empty if len(children) > 1: @@ -1352,7 +1353,7 @@ def xml_key_extractor(attr, attr_desc, data): return children[0] -class Deserializer(object): +class Deserializer: """Response object model deserializer. :param dict classes: Class type dictionary for deserializing complex types. @@ -1361,9 +1362,9 @@ class Deserializer(object): basic_types = {str: "str", int: "int", bool: "bool", float: "float"} - valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}" r"\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?") + valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?") - def __init__(self, classes: Optional[Mapping[str, type]] = None): + def __init__(self, classes: Optional[Mapping[str, type]] = None) -> None: self.deserialize_type = { "iso-8601": Deserializer.deserialize_iso, "rfc-1123": Deserializer.deserialize_rfc, @@ -1383,7 +1384,7 @@ def __init__(self, classes: Optional[Mapping[str, type]] = None): "duration": (isodate.Duration, datetime.timedelta), "iso-8601": (datetime.datetime), } - self.dependencies: Dict[str, type] = dict(classes) if classes else {} + self.dependencies: dict[str, type] = dict(classes) if classes else {} self.key_extractors = [rest_key_extractor, xml_key_extractor] # Additional properties only works if the "rest_key_extractor" is used to # extract the keys. Making it to work whatever the key extractor is too much @@ -1399,27 +1400,29 @@ def __call__(self, target_obj, response_data, content_type=None): :param str target_obj: Target data type to deserialize to. :param requests.Response response_data: REST response object. :param str content_type: Swagger "produces" if available. - :raises: DeserializationError if deserialization fails. + :raises DeserializationError: if deserialization fails. :return: Deserialized object. + :rtype: object """ data = self._unpack_content(response_data, content_type) return self._deserialize(target_obj, data) - def _deserialize(self, target_obj, data): + def _deserialize(self, target_obj, data): # pylint: disable=inconsistent-return-statements """Call the deserializer on a model. Data needs to be already deserialized as JSON or XML ElementTree :param str target_obj: Target data type to deserialize to. :param object data: Object to deserialize. - :raises: DeserializationError if deserialization fails. + :raises DeserializationError: if deserialization fails. :return: Deserialized object. + :rtype: object """ # This is already a model, go recursive just in case if hasattr(data, "_attribute_map"): constants = [name for name, config in getattr(data, "_validation", {}).items() if config.get("constant")] try: - for attr, mapconfig in data._attribute_map.items(): + for attr, mapconfig in data._attribute_map.items(): # pylint: disable=protected-access if attr in constants: continue value = getattr(data, attr) @@ -1438,13 +1441,13 @@ def _deserialize(self, target_obj, data): if isinstance(response, str): return self.deserialize_data(data, response) - elif isinstance(response, type) and issubclass(response, Enum): + if isinstance(response, type) and issubclass(response, Enum): return self.deserialize_enum(data, response) - if data is None: + if data is None or data is CoreNull: return data try: - attributes = response._attribute_map # type: ignore + attributes = response._attribute_map # type: ignore # pylint: disable=protected-access d_attrs = {} for attr, attr_desc in attributes.items(): # Check empty string. If it's not empty, someone has a real "additionalProperties"... @@ -1474,9 +1477,8 @@ def _deserialize(self, target_obj, data): except (AttributeError, TypeError, KeyError) as err: msg = "Unable to deserialize to object: " + class_name # type: ignore raise DeserializationError(msg) from err - else: - additional_properties = self._build_additional_properties(attributes, data) - return self._instantiate_model(response, d_attrs, additional_properties) + additional_properties = self._build_additional_properties(attributes, data) + return self._instantiate_model(response, d_attrs, additional_properties) def _build_additional_properties(self, attribute_map, data): if not self.additional_properties_detection: @@ -1503,6 +1505,8 @@ def _classify_target(self, target, data): :param str target: The target object type to deserialize to. :param str/dict data: The response data to deserialize. + :return: The classified target object and its class name. + :rtype: tuple """ if target is None: return None, None @@ -1514,7 +1518,7 @@ def _classify_target(self, target, data): return target, target try: - target = target._classify(data, self.dependencies) # type: ignore + target = target._classify(data, self.dependencies) # type: ignore # pylint: disable=protected-access except AttributeError: pass # Target is not a Model, no classify return target, target.__class__.__name__ # type: ignore @@ -1529,10 +1533,12 @@ def failsafe_deserialize(self, target_obj, data, content_type=None): :param str target_obj: The target object type to deserialize to. :param str/dict data: The response data to deserialize. :param str content_type: Swagger "produces" if available. + :return: Deserialized object. + :rtype: object """ try: return self(target_obj, data, content_type=content_type) - except: + except: # pylint: disable=bare-except _LOGGER.debug( "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True ) @@ -1550,10 +1556,12 @@ def _unpack_content(raw_data, content_type=None): If raw_data is something else, bypass all logic and return it directly. - :param raw_data: Data to be processed. - :param content_type: How to parse if raw_data is a string/bytes. + :param obj raw_data: Data to be processed. + :param str content_type: How to parse if raw_data is a string/bytes. :raises JSONDecodeError: If JSON is requested and parsing is impossible. :raises UnicodeDecodeError: If bytes is not UTF8 + :rtype: object + :return: Unpacked content. """ # Assume this is enough to detect a Pipeline Response without importing it context = getattr(raw_data, "context", {}) @@ -1577,24 +1585,35 @@ def _unpack_content(raw_data, content_type=None): def _instantiate_model(self, response, attrs, additional_properties=None): """Instantiate a response model passing in deserialized args. - :param response: The response model class. - :param d_attrs: The deserialized response attributes. + :param Response response: The response model class. + :param dict attrs: The deserialized response attributes. + :param dict additional_properties: Additional properties to be set. + :rtype: Response + :return: The instantiated response model. """ if callable(response): subtype = getattr(response, "_subtype_map", {}) try: - readonly = [k for k, v in response._validation.items() if v.get("readonly")] - const = [k for k, v in response._validation.items() if v.get("constant")] + readonly = [ + k + for k, v in response._validation.items() # pylint: disable=protected-access # type: ignore + if v.get("readonly") + ] + const = [ + k + for k, v in response._validation.items() # pylint: disable=protected-access # type: ignore + if v.get("constant") + ] kwargs = {k: v for k, v in attrs.items() if k not in subtype and k not in readonly + const} response_obj = response(**kwargs) for attr in readonly: setattr(response_obj, attr, attrs.get(attr)) if additional_properties: - response_obj.additional_properties = additional_properties + response_obj.additional_properties = additional_properties # type: ignore return response_obj except TypeError as err: msg = "Unable to deserialize {} into model {}. ".format(kwargs, response) # type: ignore - raise DeserializationError(msg + str(err)) + raise DeserializationError(msg + str(err)) from err else: try: for attr, value in attrs.items(): @@ -1603,15 +1622,16 @@ def _instantiate_model(self, response, attrs, additional_properties=None): except Exception as exp: msg = "Unable to populate response model. " msg += "Type: {}, Error: {}".format(type(response), exp) - raise DeserializationError(msg) + raise DeserializationError(msg) from exp - def deserialize_data(self, data, data_type): + def deserialize_data(self, data, data_type): # pylint: disable=too-many-return-statements """Process data for deserialization according to data type. :param str data: The response string to be deserialized. :param str data_type: The type to deserialize to. - :raises: DeserializationError if deserialization fails. + :raises DeserializationError: if deserialization fails. :return: Deserialized object. + :rtype: object """ if data is None: return data @@ -1625,7 +1645,11 @@ def deserialize_data(self, data, data_type): if isinstance(data, self.deserialize_expected_types.get(data_type, tuple())): return data - is_a_text_parsing_type = lambda x: x not in ["object", "[]", r"{}"] + is_a_text_parsing_type = lambda x: x not in [ # pylint: disable=unnecessary-lambda-assignment + "object", + "[]", + r"{}", + ] if isinstance(data, ET.Element) and is_a_text_parsing_type(data_type) and not data.text: return None data_val = self.deserialize_type[data_type](data) @@ -1645,14 +1669,14 @@ def deserialize_data(self, data, data_type): msg = "Unable to deserialize response data." msg += " Data: {}, {}".format(data, data_type) raise DeserializationError(msg) from err - else: - return self._deserialize(obj_type, data) + return self._deserialize(obj_type, data) def deserialize_iter(self, attr, iter_type): """Deserialize an iterable. :param list attr: Iterable to be deserialized. :param str iter_type: The type of object in the iterable. + :return: Deserialized iterable. :rtype: list """ if attr is None: @@ -1669,6 +1693,7 @@ def deserialize_dict(self, attr, dict_type): :param dict/list attr: Dictionary to be deserialized. Also accepts a list of key, value pairs. :param str dict_type: The object type of the items in the dictionary. + :return: Deserialized dictionary. :rtype: dict """ if isinstance(attr, list): @@ -1679,13 +1704,14 @@ def deserialize_dict(self, attr, dict_type): attr = {el.tag: el.text for el in attr} return {k: self.deserialize_data(v, dict_type) for k, v in attr.items()} - def deserialize_object(self, attr, **kwargs): + def deserialize_object(self, attr, **kwargs): # pylint: disable=too-many-return-statements """Deserialize a generic object. This will be handled as a dictionary. :param dict attr: Dictionary to be deserialized. + :return: Deserialized object. :rtype: dict - :raises: TypeError if non-builtin datatype encountered. + :raises TypeError: if non-builtin datatype encountered. """ if attr is None: return None @@ -1718,11 +1744,10 @@ def deserialize_object(self, attr, **kwargs): pass return deserialized - else: - error = "Cannot deserialize generic object with type: " - raise TypeError(error + str(obj_type)) + error = "Cannot deserialize generic object with type: " + raise TypeError(error + str(obj_type)) - def deserialize_basic(self, attr, data_type): + def deserialize_basic(self, attr, data_type): # pylint: disable=too-many-return-statements """Deserialize basic builtin data type from string. Will attempt to convert to str, int, float and bool. This function will also accept '1', '0', 'true' and 'false' as @@ -1730,8 +1755,9 @@ def deserialize_basic(self, attr, data_type): :param str attr: response string to be deserialized. :param str data_type: deserialization data type. + :return: Deserialized basic type. :rtype: str, int, float or bool - :raises: TypeError if string format is not valid. + :raises TypeError: if string format is not valid. """ # If we're here, data is supposed to be a basic type. # If it's still an XML node, take the text @@ -1741,24 +1767,23 @@ def deserialize_basic(self, attr, data_type): if data_type == "str": # None or '', node is empty string. return "" - else: - # None or '', node with a strong type is None. - # Don't try to model "empty bool" or "empty int" - return None + # None or '', node with a strong type is None. + # Don't try to model "empty bool" or "empty int" + return None if data_type == "bool": if attr in [True, False, 1, 0]: return bool(attr) - elif isinstance(attr, str): + if isinstance(attr, str): if attr.lower() in ["true", "1"]: return True - elif attr.lower() in ["false", "0"]: + if attr.lower() in ["false", "0"]: return False raise TypeError("Invalid boolean value: {}".format(attr)) if data_type == "str": return self.deserialize_unicode(attr) - return eval(data_type)(attr) # nosec + return eval(data_type)(attr) # nosec # pylint: disable=eval-used @staticmethod def deserialize_unicode(data): @@ -1766,6 +1791,7 @@ def deserialize_unicode(data): as a string. :param str data: response string to be deserialized. + :return: Deserialized string. :rtype: str or unicode """ # We might be here because we have an enum modeled as string, @@ -1779,8 +1805,7 @@ def deserialize_unicode(data): return data except NameError: return str(data) - else: - return str(data) + return str(data) @staticmethod def deserialize_enum(data, enum_obj): @@ -1792,6 +1817,7 @@ def deserialize_enum(data, enum_obj): :param str data: Response string to be deserialized. If this value is None or invalid it will be returned as-is. :param Enum enum_obj: Enum object to deserialize to. + :return: Deserialized enum object. :rtype: Enum """ if isinstance(data, enum_obj) or data is None: @@ -1802,9 +1828,9 @@ def deserialize_enum(data, enum_obj): # Workaround. We might consider remove it in the future. try: return list(enum_obj.__members__.values())[data] - except IndexError: + except IndexError as exc: error = "{!r} is not a valid index for enum {!r}" - raise DeserializationError(error.format(data, enum_obj)) + raise DeserializationError(error.format(data, enum_obj)) from exc try: return enum_obj(str(data)) except ValueError: @@ -1820,8 +1846,9 @@ def deserialize_bytearray(attr): """Deserialize string into bytearray. :param str attr: response string to be deserialized. + :return: Deserialized bytearray :rtype: bytearray - :raises: TypeError if string format invalid. + :raises TypeError: if string format invalid. """ if isinstance(attr, ET.Element): attr = attr.text @@ -1832,8 +1859,9 @@ def deserialize_base64(attr): """Deserialize base64 encoded string into string. :param str attr: response string to be deserialized. + :return: Deserialized base64 string :rtype: bytearray - :raises: TypeError if string format invalid. + :raises TypeError: if string format invalid. """ if isinstance(attr, ET.Element): attr = attr.text @@ -1847,8 +1875,9 @@ def deserialize_decimal(attr): """Deserialize string into Decimal object. :param str attr: response string to be deserialized. - :rtype: Decimal - :raises: DeserializationError if string format invalid. + :return: Deserialized decimal + :raises DeserializationError: if string format invalid. + :rtype: decimal """ if isinstance(attr, ET.Element): attr = attr.text @@ -1863,8 +1892,9 @@ def deserialize_long(attr): """Deserialize string into long (Py2) or int (Py3). :param str attr: response string to be deserialized. + :return: Deserialized int :rtype: long or int - :raises: ValueError if string format invalid. + :raises ValueError: if string format invalid. """ if isinstance(attr, ET.Element): attr = attr.text @@ -1875,8 +1905,9 @@ def deserialize_duration(attr): """Deserialize ISO-8601 formatted string into TimeDelta object. :param str attr: response string to be deserialized. + :return: Deserialized duration :rtype: TimeDelta - :raises: DeserializationError if string format invalid. + :raises DeserializationError: if string format invalid. """ if isinstance(attr, ET.Element): attr = attr.text @@ -1885,16 +1916,16 @@ def deserialize_duration(attr): except (ValueError, OverflowError, AttributeError) as err: msg = "Cannot deserialize duration object." raise DeserializationError(msg) from err - else: - return duration + return duration @staticmethod def deserialize_date(attr): """Deserialize ISO-8601 formatted string into Date object. :param str attr: response string to be deserialized. + :return: Deserialized date :rtype: Date - :raises: DeserializationError if string format invalid. + :raises DeserializationError: if string format invalid. """ if isinstance(attr, ET.Element): attr = attr.text @@ -1908,8 +1939,9 @@ def deserialize_time(attr): """Deserialize ISO-8601 formatted string into time object. :param str attr: response string to be deserialized. + :return: Deserialized time :rtype: datetime.time - :raises: DeserializationError if string format invalid. + :raises DeserializationError: if string format invalid. """ if isinstance(attr, ET.Element): attr = attr.text @@ -1922,31 +1954,32 @@ def deserialize_rfc(attr): """Deserialize RFC-1123 formatted string into Datetime object. :param str attr: response string to be deserialized. + :return: Deserialized RFC datetime :rtype: Datetime - :raises: DeserializationError if string format invalid. + :raises DeserializationError: if string format invalid. """ if isinstance(attr, ET.Element): attr = attr.text try: parsed_date = email.utils.parsedate_tz(attr) # type: ignore date_obj = datetime.datetime( - *parsed_date[:6], tzinfo=_FixedOffset(datetime.timedelta(minutes=(parsed_date[9] or 0) / 60)) + *parsed_date[:6], tzinfo=datetime.timezone(datetime.timedelta(minutes=(parsed_date[9] or 0) / 60)) ) if not date_obj.tzinfo: date_obj = date_obj.astimezone(tz=TZ_UTC) except ValueError as err: msg = "Cannot deserialize to rfc datetime object." raise DeserializationError(msg) from err - else: - return date_obj + return date_obj @staticmethod def deserialize_iso(attr): """Deserialize ISO-8601 formatted string into Datetime object. :param str attr: response string to be deserialized. + :return: Deserialized ISO datetime :rtype: Datetime - :raises: DeserializationError if string format invalid. + :raises DeserializationError: if string format invalid. """ if isinstance(attr, ET.Element): attr = attr.text @@ -1974,8 +2007,7 @@ def deserialize_iso(attr): except (ValueError, OverflowError, AttributeError) as err: msg = "Cannot deserialize datetime object." raise DeserializationError(msg) from err - else: - return date_obj + return date_obj @staticmethod def deserialize_unix(attr): @@ -1983,8 +2015,9 @@ def deserialize_unix(attr): This is represented as seconds. :param int attr: Object to be serialized. + :return: Deserialized datetime :rtype: Datetime - :raises: DeserializationError if format invalid + :raises DeserializationError: if format invalid """ if isinstance(attr, ET.Element): attr = int(attr.text) # type: ignore @@ -1994,5 +2027,4 @@ def deserialize_unix(attr): except ValueError as err: msg = "Cannot deserialize to unix datetime object." raise DeserializationError(msg) from err - else: - return date_obj + return date_obj diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_vendor.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/utils.py similarity index 58% rename from sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_vendor.py rename to sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/utils.py index 41f0a196726c..35c9c836f85f 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_vendor.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_utils/utils.py @@ -1,26 +1,25 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- from abc import ABC -from typing import TYPE_CHECKING - -from ._configuration import AuthoringClientConfiguration +from typing import Generic, TYPE_CHECKING, TypeVar if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports - from azure.core import PipelineClient + from .serialization import Deserializer, Serializer + - from ._serialization import Deserializer, Serializer +TClient = TypeVar("TClient") +TConfig = TypeVar("TConfig") -class AuthoringClientMixinABC(ABC): +class ClientMixinABC(ABC, Generic[TClient, TConfig]): """DO NOT use this class. It is for internal typing use only.""" - _client: "PipelineClient" - _config: AuthoringClientConfiguration + _client: TClient + _config: TConfig _serialize: "Serializer" _deserialize: "Deserializer" diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_vendor.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_vendor.py deleted file mode 100644 index 84243dfc4b1e..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_vendor.py +++ /dev/null @@ -1,26 +0,0 @@ -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from abc import ABC -from typing import TYPE_CHECKING - -from ._configuration import QuestionAnsweringClientConfiguration - -if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports - from azure.core import PipelineClient - - from ._serialization import Deserializer, Serializer - - -class QuestionAnsweringClientMixinABC(ABC): - """DO NOT use this class. It is for internal typing use only.""" - - _client: "PipelineClient" - _config: QuestionAnsweringClientConfiguration - _serialize: "Serializer" - _deserialize: "Deserializer" diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_version.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_version.py index 924647bfeefc..0e00a6283246 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_version.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/_version.py @@ -2,8 +2,8 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -VERSION = "1.1.1" +VERSION = "2.0.0b1" diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/__init__.py index 20a36d4acdc9..06848d4fd4ef 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/__init__.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/__init__.py @@ -2,18 +2,28 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position -from ._patch import QuestionAnsweringClient +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import +from ._client import QuestionAnsweringClient # type: ignore + +try: + from ._patch import __all__ as _patch_all + from ._patch import * +except ImportError: + _patch_all = [] from ._patch import patch_sdk as _patch_sdk __all__ = [ "QuestionAnsweringClient", ] - +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore _patch_sdk() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_client.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_client.py index 995a119f3fef..cbb4a3b9cd46 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_client.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_client.py @@ -2,46 +2,49 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- from copy import deepcopy -from typing import Any, Awaitable +from typing import Any, Awaitable, TYPE_CHECKING, Union +from typing_extensions import Self from azure.core import AsyncPipelineClient from azure.core.credentials import AzureKeyCredential from azure.core.pipeline import policies from azure.core.rest import AsyncHttpResponse, HttpRequest -from .. import models as _models -from .._serialization import Deserializer, Serializer +from .._utils.serialization import Deserializer, Serializer from ._configuration import QuestionAnsweringClientConfiguration -from ._operations import QuestionAnsweringClientOperationsMixin +from ._operations import _QuestionAnsweringClientOperationsMixin +if TYPE_CHECKING: + from azure.core.credentials_async import AsyncTokenCredential -class QuestionAnsweringClient( - QuestionAnsweringClientOperationsMixin -): # pylint: disable=client-accepts-api-version-keyword - """The language service API is a suite of natural language processing (NLP) skills built with - best-in-class Microsoft machine learning algorithms. The API can be used to analyze - unstructured text for tasks such as sentiment analysis, key phrase extraction, language - detection and question answering. Further documentation can be found in - https://learn.microsoft.com/azure/cognitive-services/text-analytics/overview. + +class QuestionAnsweringClient(_QuestionAnsweringClientOperationsMixin): + """QuestionAnsweringClient. :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.api.cognitiveservices.azure.com). Required. + https://.api.cognitiveservices.azure.com). Required. :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. Required. - :type credential: ~azure.core.credentials.AzureKeyCredential - :keyword api_version: Api Version. Default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. + :param credential: Credential used to authenticate requests to the service. Is either a key + credential type or a token credential type. Required. + :type credential: ~azure.core.credentials.AzureKeyCredential or + ~azure.core.credentials_async.AsyncTokenCredential + :keyword api_version: The API version to use for this operation. Default value is + "2025-05-15-preview". Note that overriding this default value may result in unsupported + behavior. :paramtype api_version: str """ - def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) -> None: + def __init__( + self, endpoint: str, credential: Union[AzureKeyCredential, "AsyncTokenCredential"], **kwargs: Any + ) -> None: _endpoint = "{Endpoint}/language" self._config = QuestionAnsweringClientConfiguration(endpoint=endpoint, credential=credential, **kwargs) + _policies = kwargs.pop("policies", None) if _policies is None: _policies = [ @@ -61,9 +64,8 @@ def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) ] self._client: AsyncPipelineClient = AsyncPipelineClient(base_url=_endpoint, policies=_policies, **kwargs) - client_models = {k: v for k, v in _models.__dict__.items() if isinstance(v, type)} - self._serialize = Serializer(client_models) - self._deserialize = Deserializer(client_models) + self._serialize = Serializer() + self._deserialize = Deserializer() self._serialize.client_side_validation = False def send_request( @@ -97,7 +99,7 @@ def send_request( async def close(self) -> None: await self._client.close() - async def __aenter__(self) -> "QuestionAnsweringClient": + async def __aenter__(self) -> Self: await self._client.__aenter__() return self diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_configuration.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_configuration.py index d5082657d55e..ce7ed937a57b 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_configuration.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_configuration.py @@ -2,36 +2,44 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -from typing import Any +from typing import Any, TYPE_CHECKING, Union from azure.core.credentials import AzureKeyCredential from azure.core.pipeline import policies from .._version import VERSION +if TYPE_CHECKING: + from azure.core.credentials_async import AsyncTokenCredential -class QuestionAnsweringClientConfiguration: # pylint: disable=too-many-instance-attributes,name-too-long + +class QuestionAnsweringClientConfiguration: # pylint: disable=too-many-instance-attributes """Configuration for QuestionAnsweringClient. Note that all parameters used to create this instance are saved as instance attributes. :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.api.cognitiveservices.azure.com). Required. + https://.api.cognitiveservices.azure.com). Required. :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. Required. - :type credential: ~azure.core.credentials.AzureKeyCredential - :keyword api_version: Api Version. Default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. + :param credential: Credential used to authenticate requests to the service. Is either a key + credential type or a token credential type. Required. + :type credential: ~azure.core.credentials.AzureKeyCredential or + ~azure.core.credentials_async.AsyncTokenCredential + :keyword api_version: The API version to use for this operation. Default value is + "2025-05-15-preview". Note that overriding this default value may result in unsupported + behavior. :paramtype api_version: str """ - def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) -> None: - api_version: str = kwargs.pop("api_version", "2021-10-01") + def __init__( + self, endpoint: str, credential: Union[AzureKeyCredential, "AsyncTokenCredential"], **kwargs: Any + ) -> None: + api_version: str = kwargs.pop("api_version", "2025-05-15-preview") if endpoint is None: raise ValueError("Parameter 'endpoint' must not be None.") @@ -41,10 +49,18 @@ def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) self.endpoint = endpoint self.credential = credential self.api_version = api_version + self.credential_scopes = kwargs.pop("credential_scopes", ["https://cognitiveservices.azure.com/.default"]) kwargs.setdefault("sdk_moniker", "ai-language-questionanswering/{}".format(VERSION)) self.polling_interval = kwargs.get("polling_interval", 30) self._configure(**kwargs) + def _infer_policy(self, **kwargs): + if isinstance(self.credential, AzureKeyCredential): + return policies.AzureKeyCredentialPolicy(self.credential, "Ocp-Apim-Subscription-Key", **kwargs) + if hasattr(self.credential, "get_token"): + return policies.AsyncBearerTokenCredentialPolicy(self.credential, *self.credential_scopes, **kwargs) + raise TypeError(f"Unsupported credential: {self.credential}") + def _configure(self, **kwargs: Any) -> None: self.user_agent_policy = kwargs.get("user_agent_policy") or policies.UserAgentPolicy(**kwargs) self.headers_policy = kwargs.get("headers_policy") or policies.HeadersPolicy(**kwargs) @@ -56,6 +72,4 @@ def _configure(self, **kwargs: Any) -> None: self.retry_policy = kwargs.get("retry_policy") or policies.AsyncRetryPolicy(**kwargs) self.authentication_policy = kwargs.get("authentication_policy") if self.credential and not self.authentication_policy: - self.authentication_policy = policies.AzureKeyCredentialPolicy( - self.credential, "Ocp-Apim-Subscription-Key", **kwargs - ) + self.authentication_policy = self._infer_policy(**kwargs) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/__init__.py index 698e94b69dbb..d3b2eadf1b56 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/__init__.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/__init__.py @@ -2,17 +2,22 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position -from ._patch import QuestionAnsweringClientOperationsMixin +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import -from ._patch import patch_sdk as _patch_sdk +from ._operations import _QuestionAnsweringClientOperationsMixin # type: ignore # pylint: disable=unused-import -__all__ = [ - "QuestionAnsweringClientOperationsMixin", -] +from ._patch import __all__ as _patch_all +from ._patch import * +from ._patch import patch_sdk as _patch_sdk +__all__ = [] +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore _patch_sdk() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/_operations.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/_operations.py index 5e370c76369b..92fab2f78b13 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/_operations.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/_operations.py @@ -1,20 +1,25 @@ -# pylint: disable=too-many-lines,too-many-statements +# pylint: disable=line-too-long,useless-suppression # coding=utf-8 # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +from collections.abc import MutableMapping from io import IOBase -from typing import Any, Callable, Dict, IO, Optional, TypeVar, Union, overload +import json +from typing import Any, Callable, IO, Optional, TypeVar, Union, overload +from azure.core import AsyncPipelineClient from azure.core.exceptions import ( ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, ResourceNotModifiedError, + StreamClosedError, + StreamConsumedError, map_error, ) from azure.core.pipeline import PipelineResponse @@ -27,17 +32,23 @@ build_question_answering_get_answers_from_text_request, build_question_answering_get_answers_request, ) -from .._vendor import QuestionAnsweringClientMixinABC +from ..._utils.model_base import SdkJSONEncoder, _deserialize, _failsafe_deserialize +from ..._utils.utils import ClientMixinABC +from .._configuration import QuestionAnsweringClientConfiguration +JSON = MutableMapping[str, Any] T = TypeVar("T") -ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, dict[str, Any]], Any]] -class QuestionAnsweringClientOperationsMixin(QuestionAnsweringClientMixinABC): +class _QuestionAnsweringClientOperationsMixin( + ClientMixinABC[AsyncPipelineClient[HttpRequest, AsyncHttpResponse], QuestionAnsweringClientConfiguration] +): + @overload async def get_answers( self, - options: _models.AnswersOptions, + knowledge_base_query_options: _models.AnswersOptions, *, project_name: str, deployment_name: str, @@ -46,10 +57,8 @@ async def get_answers( ) -> _models.AnswersResult: """Answers the specified question using your knowledge base. - Answers the specified question using your knowledge base. - - :param options: Post body of the request. Required. - :type options: ~azure.ai.language.questionanswering.models.AnswersOptions + :param knowledge_base_query_options: Post body of the request. Required. + :type knowledge_base_query_options: ~azure.ai.language.questionanswering.models.AnswersOptions :keyword project_name: The name of the project to use. Required. :paramtype project_name: str :keyword deployment_name: The name of the specific deployment of the project to use. Required. @@ -57,7 +66,7 @@ async def get_answers( :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: AnswersResult + :return: AnswersResult. The AnswersResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersResult :raises ~azure.core.exceptions.HttpResponseError: """ @@ -65,7 +74,7 @@ async def get_answers( @overload async def get_answers( self, - options: IO[bytes], + knowledge_base_query_options: JSON, *, project_name: str, deployment_name: str, @@ -74,10 +83,34 @@ async def get_answers( ) -> _models.AnswersResult: """Answers the specified question using your knowledge base. - Answers the specified question using your knowledge base. + :param knowledge_base_query_options: Post body of the request. Required. + :type knowledge_base_query_options: JSON + :keyword project_name: The name of the project to use. Required. + :paramtype project_name: str + :keyword deployment_name: The name of the specific deployment of the project to use. Required. + :paramtype deployment_name: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: AnswersResult. The AnswersResult is compatible with MutableMapping + :rtype: ~azure.ai.language.questionanswering.models.AnswersResult + :raises ~azure.core.exceptions.HttpResponseError: + """ - :param options: Post body of the request. Required. - :type options: IO[bytes] + @overload + async def get_answers( + self, + knowledge_base_query_options: IO[bytes], + *, + project_name: str, + deployment_name: str, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.AnswersResult: + """Answers the specified question using your knowledge base. + + :param knowledge_base_query_options: Post body of the request. Required. + :type knowledge_base_query_options: IO[bytes] :keyword project_name: The name of the project to use. Required. :paramtype project_name: str :keyword deployment_name: The name of the specific deployment of the project to use. Required. @@ -85,7 +118,7 @@ async def get_answers( :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: AnswersResult + :return: AnswersResult. The AnswersResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersResult :raises ~azure.core.exceptions.HttpResponseError: """ @@ -93,7 +126,7 @@ async def get_answers( @distributed_trace_async async def get_answers( self, - options: Union[_models.AnswersOptions, IO[bytes]], + knowledge_base_query_options: Union[_models.AnswersOptions, JSON, IO[bytes]], *, project_name: str, deployment_name: str, @@ -101,20 +134,19 @@ async def get_answers( ) -> _models.AnswersResult: """Answers the specified question using your knowledge base. - Answers the specified question using your knowledge base. - - :param options: Post body of the request. Is either a AnswersOptions type or a IO[bytes] type. - Required. - :type options: ~azure.ai.language.questionanswering.models.AnswersOptions or IO[bytes] + :param knowledge_base_query_options: Post body of the request. Is one of the following types: + AnswersOptions, JSON, IO[bytes] Required. + :type knowledge_base_query_options: ~azure.ai.language.questionanswering.models.AnswersOptions + or JSON or IO[bytes] :keyword project_name: The name of the project to use. Required. :paramtype project_name: str :keyword deployment_name: The name of the specific deployment of the project to use. Required. :paramtype deployment_name: str - :return: AnswersResult + :return: AnswersResult. The AnswersResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersResult :raises ~azure.core.exceptions.HttpResponseError: """ - error_map = { + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, @@ -129,19 +161,17 @@ async def get_answers( cls: ClsType[_models.AnswersResult] = kwargs.pop("cls", None) content_type = content_type or "application/json" - _json = None _content = None - if isinstance(options, (IOBase, bytes)): - _content = options + if isinstance(knowledge_base_query_options, (IOBase, bytes)): + _content = knowledge_base_query_options else: - _json = self._serialize.body(options, "AnswersOptions") + _content = json.dumps(knowledge_base_query_options, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_question_answering_get_answers_request( project_name=project_name, deployment_name=deployment_name, content_type=content_type, api_version=self._config.api_version, - json=_json, content=_content, headers=_headers, params=_params, @@ -151,7 +181,7 @@ async def get_answers( } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _stream = kwargs.pop("stream", False) pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access _request, stream=_stream, **kwargs ) @@ -160,12 +190,18 @@ async def get_answers( if response.status_code not in [200]: if _stream: - await response.read() # Load the body in memory and close the socket + try: + await response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass map_error(status_code=response.status_code, response=response, error_map=error_map) - error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + error = _failsafe_deserialize(_models.ErrorResponse, response) raise HttpResponseError(response=response, model=error) - deserialized = self._deserialize("AnswersResult", pipeline_response) + if _stream: + deserialized = response.iter_bytes() + else: + deserialized = _deserialize(_models.AnswersResult, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -174,56 +210,71 @@ async def get_answers( @overload async def get_answers_from_text( - self, options: _models.AnswersFromTextOptions, *, content_type: str = "application/json", **kwargs: Any + self, + text_query_options: _models.AnswersFromTextOptions, + *, + content_type: str = "application/json", + **kwargs: Any ) -> _models.AnswersFromTextResult: """Answers the specified question using the provided text in the body. - Answers the specified question using the provided text in the body. - - :param options: Post body of the request. Required. - :type options: ~azure.ai.language.questionanswering.models.AnswersFromTextOptions + :param text_query_options: Post body of the request. Required. + :type text_query_options: ~azure.ai.language.questionanswering.models.AnswersFromTextOptions :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str - :return: AnswersFromTextResult + :return: AnswersFromTextResult. The AnswersFromTextResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersFromTextResult :raises ~azure.core.exceptions.HttpResponseError: """ @overload async def get_answers_from_text( - self, options: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + self, text_query_options: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> _models.AnswersFromTextResult: """Answers the specified question using the provided text in the body. - Answers the specified question using the provided text in the body. + :param text_query_options: Post body of the request. Required. + :type text_query_options: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: AnswersFromTextResult. The AnswersFromTextResult is compatible with MutableMapping + :rtype: ~azure.ai.language.questionanswering.models.AnswersFromTextResult + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def get_answers_from_text( + self, text_query_options: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> _models.AnswersFromTextResult: + """Answers the specified question using the provided text in the body. - :param options: Post body of the request. Required. - :type options: IO[bytes] + :param text_query_options: Post body of the request. Required. + :type text_query_options: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str - :return: AnswersFromTextResult + :return: AnswersFromTextResult. The AnswersFromTextResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersFromTextResult :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace_async async def get_answers_from_text( - self, options: Union[_models.AnswersFromTextOptions, IO[bytes]], **kwargs: Any + self, text_query_options: Union[_models.AnswersFromTextOptions, JSON, IO[bytes]], **kwargs: Any ) -> _models.AnswersFromTextResult: """Answers the specified question using the provided text in the body. - Answers the specified question using the provided text in the body. - - :param options: Post body of the request. Is either a AnswersFromTextOptions type or a - IO[bytes] type. Required. - :type options: ~azure.ai.language.questionanswering.models.AnswersFromTextOptions or IO[bytes] - :return: AnswersFromTextResult + :param text_query_options: Post body of the request. Is one of the following types: + AnswersFromTextOptions, JSON, IO[bytes] Required. + :type text_query_options: ~azure.ai.language.questionanswering.models.AnswersFromTextOptions or + JSON or IO[bytes] + :return: AnswersFromTextResult. The AnswersFromTextResult is compatible with MutableMapping :rtype: ~azure.ai.language.questionanswering.models.AnswersFromTextResult :raises ~azure.core.exceptions.HttpResponseError: """ - error_map = { + error_map: MutableMapping = { 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, @@ -238,17 +289,15 @@ async def get_answers_from_text( cls: ClsType[_models.AnswersFromTextResult] = kwargs.pop("cls", None) content_type = content_type or "application/json" - _json = None _content = None - if isinstance(options, (IOBase, bytes)): - _content = options + if isinstance(text_query_options, (IOBase, bytes)): + _content = text_query_options else: - _json = self._serialize.body(options, "AnswersFromTextOptions") + _content = json.dumps(text_query_options, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_question_answering_get_answers_from_text_request( content_type=content_type, api_version=self._config.api_version, - json=_json, content=_content, headers=_headers, params=_params, @@ -258,7 +307,7 @@ async def get_answers_from_text( } _request.url = self._client.format_url(_request.url, **path_format_arguments) - _stream = False + _stream = kwargs.pop("stream", False) pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access _request, stream=_stream, **kwargs ) @@ -267,12 +316,18 @@ async def get_answers_from_text( if response.status_code not in [200]: if _stream: - await response.read() # Load the body in memory and close the socket + try: + await response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass map_error(status_code=response.status_code, response=response, error_map=error_map) - error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + error = _failsafe_deserialize(_models.ErrorResponse, response) raise HttpResponseError(response=response, model=error) - deserialized = self._deserialize("AnswersFromTextResult", pipeline_response) + if _stream: + deserialized = response.iter_bytes() + else: + deserialized = _deserialize(_models.AnswersFromTextResult, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/_patch.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/_patch.py index 014309ffbaf1..95586db129a9 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/_patch.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_operations/_patch.py @@ -1,7 +1,9 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ +# pylint: disable=line-too-long,useless-suppression +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- """Customize generated code here. Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize @@ -9,7 +11,7 @@ from typing import Any, List, overload, Optional, Union from azure.core.tracing.decorator_async import distributed_trace_async -from ._operations import QuestionAnsweringClientOperationsMixin as QuestionAnsweringClientOperationsMixinGenerated +from ._operations import _QuestionAnsweringClientOperationsMixin as QuestionAnsweringClientOperationsMixinGenerated from ...models import ( AnswersOptions, AnswersFromTextOptions, @@ -23,15 +25,17 @@ from ..._operations._patch import _get_answers_from_text_prepare_options, _get_answers_prepare_options -class QuestionAnsweringClientOperationsMixin(QuestionAnsweringClientOperationsMixinGenerated): +class _QuestionAnsweringClientOperationsMixin(QuestionAnsweringClientOperationsMixinGenerated): @overload # type: ignore # https://github.com/Azure/azure-sdk-for-python/issues/26621 + # pylint: disable=arguments-renamed async def get_answers( self, options: AnswersOptions, *, project_name: str, deployment_name: str, **kwargs: Any ) -> AnswersResult: """Answers the specified question using your knowledge base. - :param options: Positional only. POST body of the request. Provide either `options`, OR - individual keyword arguments. If both are provided, only the options object will be used. + :param options: Positional only. POST body of the request. + Provide either `options`, OR individual keyword arguments. + If both are provided, only the options object will be used. :type options: ~azure.ai.language.questionanswering.models.AnswersOptions :keyword project_name: The name of the knowledge base project to use. :paramtype project_name: str @@ -95,7 +99,7 @@ async def get_answers( # pylint: disable=arguments-differ """ # pylint ignore b/c with overloads we need to doc ALL the params in the impl for them to show up in docs - # pylint: disable=docstring-keyword-should-match-keyword-only,docstring-missing-param,docstring-should-be-keyword + # pylint: disable=docstring-keyword-should-match-keyword-only,docstring-missing-param,docstring-should-be-keyword,arguments-renamed @distributed_trace_async async def get_answers(self, *args, **kwargs) -> AnswersResult: # type: ignore """Answers the specified question using your knowledge base. @@ -182,6 +186,7 @@ async def get_answers_from_text( # pylint: disable=arguments-differ :raises ~azure.core.exceptions.HttpResponseError: """ + # pylint: disable=arguments-renamed @distributed_trace_async async def get_answers_from_text(self, *args, **kwargs) -> AnswersFromTextResult: # type: ignore """Answers the specified question using the provided text in the body. @@ -210,14 +215,16 @@ async def get_answers_from_text(self, *args, **kwargs) -> AnswersFromTextResult: :dedent: 4 :caption: Answers the specified question using the provided text. """ + # Use only explicit user-provided language (if any); no hidden default attribute + _explicit_language = kwargs.pop("language", None) options, kwargs = _get_answers_from_text_prepare_options( - *args, language=kwargs.pop("language", self._default_language), **kwargs # type: ignore + *args, language=_explicit_language, **kwargs # type: ignore ) return await super().get_answers_from_text(options, **kwargs) # type: ignore __all__: List[str] = [ - "QuestionAnsweringClientOperationsMixin" + "_QuestionAnsweringClientOperationsMixin" ] # Add all objects you want publicly available to users at this package level diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_patch.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_patch.py index 057b53755bab..87676c65a8f0 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_patch.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_patch.py @@ -1,77 +1,15 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- """Customize generated code here. Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize """ -from typing import List, Union, Any -from azure.core.credentials import AzureKeyCredential -from azure.core.credentials_async import AsyncTokenCredential -from azure.core.pipeline.policies import AzureKeyCredentialPolicy, AsyncBearerTokenCredentialPolicy -from ._client import QuestionAnsweringClient as QuestionAnsweringClientGenerated -def _authentication_policy(credential, **kwargs): - if credential is None: - raise ValueError("Parameter 'credential' must not be None.") - if isinstance(credential, AzureKeyCredential): - authentication_policy = AzureKeyCredentialPolicy( - name="Ocp-Apim-Subscription-Key", credential=credential, **kwargs - ) - elif hasattr(credential, "get_token"): - authentication_policy = AsyncBearerTokenCredentialPolicy( - credential, *kwargs.pop("credential_scopes", ["https://cognitiveservices.azure.com/.default"]), **kwargs - ) - else: - raise TypeError( - "Unsupported credential: {}. Use an instance of AzureKeyCredential " - "or a token credential from azure.identity".format(type(credential)) - ) - return authentication_policy - - -class QuestionAnsweringClient(QuestionAnsweringClientGenerated): - """The language service API is a suite of natural language processing (NLP) skills built with best-in-class - Microsoft machine learning algorithms. - - The API can be used to analyze unstructured text for tasks such as sentiment - analysis, key phrase extraction, language detection and question answering. - Further documentation can be found in https://learn.microsoft.com/azure/cognitive-services/language-service/overview - - :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.cognitiveservices.azure.com). - :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. - This can be the an instance of AzureKeyCredential if using a Language API key - or a token credential from :mod:`azure.identity`. - :type credential: ~azure.core.credentials.AzureKeyCredential or ~azure.core.credentials_async.AsyncTokenCredential - :keyword str default_language: Sets the default language to use for all operations. - :keyword api_version: Api Version. Default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. - :paramtype api_version: str - """ - - def __init__( - self, endpoint: str, credential: Union[AzureKeyCredential, AsyncTokenCredential], **kwargs: Any - ) -> None: - try: - endpoint = endpoint.rstrip("/") - except AttributeError as exc: - raise ValueError("Parameter 'endpoint' must be a string.") from exc - super().__init__( - endpoint=endpoint, - credential=credential, # type: ignore - authentication_policy=kwargs.pop("authentication_policy", _authentication_policy(credential)), - **kwargs - ) - self._default_language = kwargs.pop("default_language", None) - - -__all__: List[str] = [ - "QuestionAnsweringClient" -] # Add all objects you want publicly available to users at this package level +__all__: list[str] = [] # Add all objects you want publicly available to users at this package level def patch_sdk(): diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_vendor.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_vendor.py deleted file mode 100644 index 5e5fc9a83ad1..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/aio/_vendor.py +++ /dev/null @@ -1,26 +0,0 @@ -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from abc import ABC -from typing import TYPE_CHECKING - -from ._configuration import QuestionAnsweringClientConfiguration - -if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports - from azure.core import AsyncPipelineClient - - from .._serialization import Deserializer, Serializer - - -class QuestionAnsweringClientMixinABC(ABC): - """DO NOT use this class. It is for internal typing use only.""" - - _client: "AsyncPipelineClient" - _config: QuestionAnsweringClientConfiguration - _serialize: "Serializer" - _deserialize: "Deserializer" diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/__init__.py deleted file mode 100644 index 7ab43d859323..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from ._client import AuthoringClient -from ._version import VERSION - -__version__ = VERSION - -try: - from ._patch import __all__ as _patch_all - from ._patch import * # pylint: disable=unused-wildcard-import -except ImportError: - _patch_all = [] -from ._patch import patch_sdk as _patch_sdk - -__all__ = [ - "AuthoringClient", -] -__all__.extend([p for p in _patch_all if p not in __all__]) - -_patch_sdk() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_client.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_client.py deleted file mode 100644 index 924438c0134c..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_client.py +++ /dev/null @@ -1,101 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from copy import deepcopy -from typing import Any - -from azure.core import PipelineClient -from azure.core.credentials import AzureKeyCredential -from azure.core.pipeline import policies -from azure.core.rest import HttpRequest, HttpResponse - -from ._configuration import AuthoringClientConfiguration -from ._operations import AuthoringClientOperationsMixin -from ._serialization import Deserializer, Serializer - - -class AuthoringClient(AuthoringClientOperationsMixin): # pylint: disable=client-accepts-api-version-keyword - """The language service API is a suite of natural language processing (NLP) skills built with - best-in-class Microsoft machine learning algorithms. The API can be used to analyze - unstructured text for tasks such as sentiment analysis, key phrase extraction, language - detection and question answering. Further documentation can be found in - https://learn.microsoft.com/azure/cognitive-services/text-analytics/overview. - - :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.api.cognitiveservices.azure.com). Required. - :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. Required. - :type credential: ~azure.core.credentials.AzureKeyCredential - :keyword api_version: Api Version. Default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. - :paramtype api_version: str - :keyword int polling_interval: Default waiting time between two polls for LRO operations if no - Retry-After header is present. - """ - - def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) -> None: - _endpoint = "{Endpoint}/language" - self._config = AuthoringClientConfiguration(endpoint=endpoint, credential=credential, **kwargs) - _policies = kwargs.pop("policies", None) - if _policies is None: - _policies = [ - policies.RequestIdPolicy(**kwargs), - self._config.headers_policy, - self._config.user_agent_policy, - self._config.proxy_policy, - policies.ContentDecodePolicy(**kwargs), - self._config.redirect_policy, - self._config.retry_policy, - self._config.authentication_policy, - self._config.custom_hook_policy, - self._config.logging_policy, - policies.DistributedTracingPolicy(**kwargs), - policies.SensitiveHeaderCleanupPolicy(**kwargs) if self._config.redirect_policy else None, - self._config.http_logging_policy, - ] - self._client: PipelineClient = PipelineClient(base_url=_endpoint, policies=_policies, **kwargs) - - self._serialize = Serializer() - self._deserialize = Deserializer() - self._serialize.client_side_validation = False - - def send_request(self, request: HttpRequest, *, stream: bool = False, **kwargs: Any) -> HttpResponse: - """Runs the network request through the client's chained policies. - - >>> from azure.core.rest import HttpRequest - >>> request = HttpRequest("GET", "https://www.example.org/") - - >>> response = client.send_request(request) - - - For more information on this code flow, see https://aka.ms/azsdk/dpcodegen/python/send_request - - :param request: The network request you want to make. Required. - :type request: ~azure.core.rest.HttpRequest - :keyword bool stream: Whether the response payload will be streamed. Defaults to False. - :return: The response of your network call. Does not do error handling on your response. - :rtype: ~azure.core.rest.HttpResponse - """ - - request_copy = deepcopy(request) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments) - return self._client.send_request(request_copy, stream=stream, **kwargs) # type: ignore - - def close(self) -> None: - self._client.close() - - def __enter__(self) -> "AuthoringClient": - self._client.__enter__() - return self - - def __exit__(self, *exc_details: Any) -> None: - self._client.__exit__(*exc_details) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_configuration.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_configuration.py deleted file mode 100644 index 537dea250383..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_configuration.py +++ /dev/null @@ -1,61 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from typing import Any - -from azure.core.credentials import AzureKeyCredential -from azure.core.pipeline import policies - -from ._version import VERSION - - -class AuthoringClientConfiguration: # pylint: disable=too-many-instance-attributes,name-too-long - """Configuration for AuthoringClient. - - Note that all parameters used to create this instance are saved as instance - attributes. - - :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.api.cognitiveservices.azure.com). Required. - :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. Required. - :type credential: ~azure.core.credentials.AzureKeyCredential - :keyword api_version: Api Version. Default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. - :paramtype api_version: str - """ - - def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) -> None: - api_version: str = kwargs.pop("api_version", "2021-10-01") - - if endpoint is None: - raise ValueError("Parameter 'endpoint' must not be None.") - if credential is None: - raise ValueError("Parameter 'credential' must not be None.") - - self.endpoint = endpoint - self.credential = credential - self.api_version = api_version - kwargs.setdefault("sdk_moniker", "ai-language-questionanswering/{}".format(VERSION)) - self.polling_interval = kwargs.get("polling_interval", 30) - self._configure(**kwargs) - - def _configure(self, **kwargs: Any) -> None: - self.user_agent_policy = kwargs.get("user_agent_policy") or policies.UserAgentPolicy(**kwargs) - self.headers_policy = kwargs.get("headers_policy") or policies.HeadersPolicy(**kwargs) - self.proxy_policy = kwargs.get("proxy_policy") or policies.ProxyPolicy(**kwargs) - self.logging_policy = kwargs.get("logging_policy") or policies.NetworkTraceLoggingPolicy(**kwargs) - self.http_logging_policy = kwargs.get("http_logging_policy") or policies.HttpLoggingPolicy(**kwargs) - self.custom_hook_policy = kwargs.get("custom_hook_policy") or policies.CustomHookPolicy(**kwargs) - self.redirect_policy = kwargs.get("redirect_policy") or policies.RedirectPolicy(**kwargs) - self.retry_policy = kwargs.get("retry_policy") or policies.RetryPolicy(**kwargs) - self.authentication_policy = kwargs.get("authentication_policy") - if self.credential and not self.authentication_policy: - self.authentication_policy = policies.AzureKeyCredentialPolicy( - self.credential, "Ocp-Apim-Subscription-Key", **kwargs - ) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_operations/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_operations/__init__.py deleted file mode 100644 index c937d5bb246a..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_operations/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from ._operations import AuthoringClientOperationsMixin - -from ._patch import __all__ as _patch_all -from ._patch import * # pylint: disable=unused-wildcard-import -from ._patch import patch_sdk as _patch_sdk - -__all__ = [ - "AuthoringClientOperationsMixin", -] -__all__.extend([p for p in _patch_all if p not in __all__]) -_patch_sdk() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_operations/_operations.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_operations/_operations.py deleted file mode 100644 index 47be8ce3d8d7..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_operations/_operations.py +++ /dev/null @@ -1,3779 +0,0 @@ -# pylint: disable=too-many-lines,too-many-statements -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- -from io import IOBase -import sys -from typing import Any, Callable, Dict, IO, Iterable, List, Optional, TypeVar, Union, cast, overload -import urllib.parse - -from azure.core.exceptions import ( - ClientAuthenticationError, - HttpResponseError, - ResourceExistsError, - ResourceNotFoundError, - ResourceNotModifiedError, - map_error, -) -from azure.core.paging import ItemPaged -from azure.core.pipeline import PipelineResponse -from azure.core.polling import LROPoller, NoPolling, PollingMethod -from azure.core.polling.base_polling import LROBasePolling -from azure.core.rest import HttpRequest, HttpResponse -from azure.core.tracing.decorator import distributed_trace -from azure.core.utils import case_insensitive_dict - -from .._serialization import Serializer -from .._vendor import AuthoringClientMixinABC - -if sys.version_info >= (3, 9): - from collections.abc import MutableMapping -else: - from typing import MutableMapping # type: ignore # pylint: disable=ungrouped-imports -JSON = MutableMapping[str, Any] # pylint: disable=unsubscriptable-object -T = TypeVar("T") -ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] - -_SERIALIZER = Serializer() -_SERIALIZER.client_side_validation = False - - -def build_authoring_list_projects_request( - *, top: Optional[int] = None, skip: Optional[int] = None, maxpagesize: Optional[int] = None, **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects" - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - if top is not None: - _params["top"] = _SERIALIZER.query("top", top, "int") - if skip is not None: - _params["skip"] = _SERIALIZER.query("skip", skip, "int") - if maxpagesize is not None: - _params["maxpagesize"] = _SERIALIZER.query("maxpagesize", maxpagesize, "int") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_get_project_details_request( # pylint: disable=name-too-long - project_name: str, **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_create_project_request(project_name: str, **kwargs: Any) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="PATCH", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_delete_project_request(project_name: str, **kwargs: Any) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_export_request( - project_name: str, *, file_format: str = "json", asset_kind: Optional[str] = None, **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}/:export" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - if file_format is not None: - _params["format"] = _SERIALIZER.query("file_format", file_format, "str") - if asset_kind is not None: - _params["assetKind"] = _SERIALIZER.query("asset_kind", asset_kind, "str") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_import_assets_request( - project_name: str, *, file_format: str = "json", asset_kind: Optional[str] = None, **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}/:import" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - if file_format is not None: - _params["format"] = _SERIALIZER.query("file_format", file_format, "str") - if asset_kind is not None: - _params["assetKind"] = _SERIALIZER.query("asset_kind", asset_kind, "str") - - # Construct headers - if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_deploy_project_request(project_name: str, deployment_name: str, **kwargs: Any) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}/deployments/{deploymentName}" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - "deploymentName": _SERIALIZER.url("deployment_name", deployment_name, "str"), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="PUT", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_list_deployments_request( - project_name: str, - *, - top: Optional[int] = None, - skip: Optional[int] = None, - maxpagesize: Optional[int] = None, - **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}/deployments" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - if top is not None: - _params["top"] = _SERIALIZER.query("top", top, "int") - if skip is not None: - _params["skip"] = _SERIALIZER.query("skip", skip, "int") - if maxpagesize is not None: - _params["maxpagesize"] = _SERIALIZER.query("maxpagesize", maxpagesize, "int") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_list_synonyms_request( - project_name: str, - *, - top: Optional[int] = None, - skip: Optional[int] = None, - maxpagesize: Optional[int] = None, - **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}/synonyms" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - if top is not None: - _params["top"] = _SERIALIZER.query("top", top, "int") - if skip is not None: - _params["skip"] = _SERIALIZER.query("skip", skip, "int") - if maxpagesize is not None: - _params["maxpagesize"] = _SERIALIZER.query("maxpagesize", maxpagesize, "int") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_update_synonyms_request(project_name: str, **kwargs: Any) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}/synonyms" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="PUT", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_list_sources_request( - project_name: str, - *, - top: Optional[int] = None, - skip: Optional[int] = None, - maxpagesize: Optional[int] = None, - **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}/sources" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - if top is not None: - _params["top"] = _SERIALIZER.query("top", top, "int") - if skip is not None: - _params["skip"] = _SERIALIZER.query("skip", skip, "int") - if maxpagesize is not None: - _params["maxpagesize"] = _SERIALIZER.query("maxpagesize", maxpagesize, "int") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_update_sources_request(project_name: str, **kwargs: Any) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}/sources" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="PATCH", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_list_qnas_request( - project_name: str, - *, - source: Optional[str] = None, - top: Optional[int] = None, - skip: Optional[int] = None, - maxpagesize: Optional[int] = None, - **kwargs: Any -) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}/qnas" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - if source is not None: - _params["source"] = _SERIALIZER.query("source", source, "str") - if top is not None: - _params["top"] = _SERIALIZER.query("top", top, "int") - if skip is not None: - _params["skip"] = _SERIALIZER.query("skip", skip, "int") - if maxpagesize is not None: - _params["maxpagesize"] = _SERIALIZER.query("maxpagesize", maxpagesize, "int") - - # Construct headers - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_update_qnas_request(project_name: str, **kwargs: Any) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}/qnas" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="PATCH", url=_url, params=_params, headers=_headers, **kwargs) - - -def build_authoring_add_feedback_request(project_name: str, **kwargs: Any) -> HttpRequest: - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2021-10-01")) - accept = _headers.pop("Accept", "application/json") - - # Construct URL - _url = "/query-knowledgebases/projects/{projectName}/feedback" - path_format_arguments = { - "projectName": _SERIALIZER.url("project_name", project_name, "str", max_length=100), - } - - _url: str = _url.format(**path_format_arguments) # type: ignore - - # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") - - # Construct headers - if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") - _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - - return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) - - -class AuthoringClientOperationsMixin(AuthoringClientMixinABC): # pylint: disable=too-many-public-methods - @distributed_trace - def list_projects(self, *, top: Optional[int] = None, skip: Optional[int] = None, **kwargs: Any) -> Iterable[JSON]: - # pylint: disable=line-too-long - """Gets all projects for a user. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/list-projects - for more information. - - :keyword top: The maximum number of resources to return from the collection. Default value is - None. - :paramtype top: int - :keyword skip: An offset into the collection of the first resource to be returned. Default - value is None. - :paramtype skip: int - :return: An iterator like instance of JSON object - :rtype: ~azure.core.paging.ItemPaged[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Optional. Project creation - date-time. - "description": "str", # Optional. Description of the project. - "language": "str", # Optional. Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. - "lastDeployedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last deployment date-time. - "lastModifiedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last modified date-time. - "multilingualResource": bool, # Optional. Resource enabled for multiple - languages across projects or not. - "projectName": "str", # Optional. Name of the project. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - maxpagesize = kwargs.pop("maxpagesize", None) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_list_projects_request( - top=top, - skip=skip, - maxpagesize=maxpagesize, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) - - def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return ItemPaged(get_next, extract_data) - - @distributed_trace - def get_project_details(self, project_name: str, **kwargs: Any) -> JSON: - # pylint: disable=line-too-long - """Get the requested project metadata. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/get-project-details - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :return: JSON object - :rtype: JSON - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Optional. Project creation - date-time. - "description": "str", # Optional. Description of the project. - "language": "str", # Optional. Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. - "lastDeployedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last deployment date-time. - "lastModifiedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last modified date-time. - "multilingualResource": bool, # Optional. Resource enabled for multiple - languages across projects or not. - "projectName": "str", # Optional. Name of the project. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - """ - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[JSON] = kwargs.pop("cls", None) - - _request = build_authoring_get_project_details_request( - project_name=project_name, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if response.content: - deserialized = response.json() - else: - deserialized = None - - if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore - - return cast(JSON, deserialized) # type: ignore - - @overload - def create_project( - self, project_name: str, options: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> JSON: - # pylint: disable=line-too-long - """Create or update a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/create-project - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Parameters needed to create the project. Required. - :type options: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: JSON object - :rtype: JSON - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - options = { - "language": "str", # Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. Required. - "description": "str", # Optional. Description of the project. - "multilingualResource": bool, # Optional. Set to true to enable creating - knowledgebases in different languages for the same resource. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - - # response body for status code(s): 200, 201 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Optional. Project creation - date-time. - "description": "str", # Optional. Description of the project. - "language": "str", # Optional. Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. - "lastDeployedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last deployment date-time. - "lastModifiedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last modified date-time. - "multilingualResource": bool, # Optional. Resource enabled for multiple - languages across projects or not. - "projectName": "str", # Optional. Name of the project. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - """ - - @overload - def create_project( - self, project_name: str, options: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> JSON: - # pylint: disable=line-too-long - """Create or update a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/create-project - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Parameters needed to create the project. Required. - :type options: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: JSON object - :rtype: JSON - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200, 201 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Optional. Project creation - date-time. - "description": "str", # Optional. Description of the project. - "language": "str", # Optional. Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. - "lastDeployedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last deployment date-time. - "lastModifiedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last modified date-time. - "multilingualResource": bool, # Optional. Resource enabled for multiple - languages across projects or not. - "projectName": "str", # Optional. Name of the project. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - """ - - @distributed_trace - def create_project(self, project_name: str, options: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: - # pylint: disable=line-too-long - """Create or update a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/create-project - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Parameters needed to create the project. Is either a JSON type or a IO[bytes] - type. Required. - :type options: JSON or IO[bytes] - :return: JSON object - :rtype: JSON - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - options = { - "language": "str", # Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. Required. - "description": "str", # Optional. Description of the project. - "multilingualResource": bool, # Optional. Set to true to enable creating - knowledgebases in different languages for the same resource. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - - # response body for status code(s): 200, 201 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Optional. Project creation - date-time. - "description": "str", # Optional. Description of the project. - "language": "str", # Optional. Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. - "lastDeployedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last deployment date-time. - "lastModifiedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last modified date-time. - "multilingualResource": bool, # Optional. Resource enabled for multiple - languages across projects or not. - "projectName": "str", # Optional. Name of the project. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - """ - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(options, (IOBase, bytes)): - _content = options - else: - _json = options - - _request = build_authoring_create_project_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 201]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 201: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore - - return cast(JSON, deserialized) # type: ignore - - def _delete_project_initial( # pylint: disable=inconsistent-return-statements - self, project_name: str, **kwargs: Any - ) -> None: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[None] = kwargs.pop("cls", None) - - _request = build_authoring_delete_project_request( - project_name=project_name, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [202]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - response_headers = {} - response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) - - if cls: - return cls(pipeline_response, None, response_headers) # type: ignore - - @distributed_trace - def begin_delete_project(self, project_name: str, **kwargs: Any) -> LROPoller[None]: - """Delete the project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/delete-project - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :return: An instance of LROPoller that returns None - :rtype: ~azure.core.polling.LROPoller[None] - :raises ~azure.core.exceptions.HttpResponseError: - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[None] = kwargs.pop("cls", None) - polling: Union[bool, PollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = self._delete_project_initial( # type: ignore - project_name=project_name, cls=lambda x, y, z: x, headers=_headers, params=_params, **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): # pylint: disable=inconsistent-return-statements - if cls: - return cls(pipeline_response, None, {}) # type: ignore - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: PollingMethod = cast( - PollingMethod, LROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) - ) - elif polling is False: - polling_method = cast(PollingMethod, NoPolling()) - else: - polling_method = polling - if cont_token: - return LROPoller[None].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return LROPoller[None](self._client, raw_result, get_long_running_output, polling_method) # type: ignore - - def _export_initial( - self, project_name: str, *, file_format: str = "json", asset_kind: Optional[str] = None, **kwargs: Any - ) -> Optional[JSON]: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) - - _request = build_authoring_export_request( - project_name=project_name, - file_format=file_format, - asset_kind=asset_kind, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 202]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - deserialized = None - response_headers = {} - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 202: - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) - - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore - - @distributed_trace - def begin_export( - self, project_name: str, *, file_format: str = "json", asset_kind: Optional[str] = None, **kwargs: Any - ) -> LROPoller[JSON]: - # pylint: disable=line-too-long - """Export project metadata and assets. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/export - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :keyword file_format: Knowledge base Import or Export format. Known values are: "json", "tsv", - and "excel". Default value is "json". - :paramtype file_format: str - :keyword asset_kind: Kind of the asset of the project. Known values are: "qnas" and "synonyms". - Default value is None. - :paramtype asset_kind: str - :return: An instance of LROPoller that returns JSON object - :rtype: ~azure.core.polling.LROPoller[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Required. - "jobId": "str", # Required. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Required. - "resultUrl": "str", # URL to download the result of the Export Job. - Required. - "status": "str", # Job Status. Required. Known values are: "notStarted", - "running", "succeeded", "failed", "cancelled", "cancelling", and - "partiallyCompleted". - "errors": [ - { - "code": "str", # One of a server-defined set of error codes. - Required. Known values are: "InvalidRequest", "InvalidArgument", - "Unauthorized", "Forbidden", "NotFound", "ProjectNotFound", - "OperationNotFound", "AzureCognitiveSearchNotFound", - "AzureCognitiveSearchIndexNotFound", "TooManyRequests", - "AzureCognitiveSearchThrottling", - "AzureCognitiveSearchIndexLimitReached", "InternalServerError", and - "ServiceUnavailable". - "message": "str", # A human-readable representation of the - error. Required. - "details": [ - ... - ], - "innererror": { - "code": "str", # One of a server-defined set of - error codes. Required. Known values are: "InvalidRequest", - "InvalidParameterValue", "KnowledgeBaseNotFound", - "AzureCognitiveSearchNotFound", "AzureCognitiveSearchThrottling", and - "ExtractionFailure". - "message": "str", # Error message. Required. - "details": { - "str": "str" # Optional. Error details. - }, - "innererror": ..., - "target": "str" # Optional. Error target. - }, - "target": "str" # Optional. The target of the error. - } - ], - "expirationDateTime": "2020-02-20 00:00:00" # Optional. - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[JSON] = kwargs.pop("cls", None) - polling: Union[bool, PollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = self._export_initial( - project_name=project_name, - file_format=file_format, - asset_kind=asset_kind, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): - response = pipeline_response.http_response - if response.content: - deserialized = response.json() - else: - deserialized = None - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore - return deserialized - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: PollingMethod = cast( - PollingMethod, LROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) - ) - elif polling is False: - polling_method = cast(PollingMethod, NoPolling()) - else: - polling_method = polling - if cont_token: - return LROPoller[JSON].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return LROPoller[JSON](self._client, raw_result, get_long_running_output, polling_method) # type: ignore - - def _import_assets_initial( - self, - project_name: str, - options: Optional[Union[JSON, IO[bytes]]] = None, - *, - file_format: str = "json", - asset_kind: Optional[str] = None, - **kwargs: Any - ) -> Optional[JSON]: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(options, (IOBase, bytes)): - _content = options - else: - if options is not None: - _json = options - else: - _json = None - - _request = build_authoring_import_assets_request( - project_name=project_name, - file_format=file_format, - asset_kind=asset_kind, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 202]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - deserialized = None - response_headers = {} - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 202: - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) - - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore - - @overload - def begin_import_assets( - self, - project_name: str, - options: Optional[JSON] = None, - *, - file_format: str = "json", - asset_kind: Optional[str] = None, - content_type: str = "application/json", - **kwargs: Any - ) -> LROPoller[JSON]: - # pylint: disable=line-too-long - """Import project assets. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/import - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Project assets the needs to be imported. Default value is None. - :type options: JSON - :keyword file_format: Knowledge base Import or Export format. Known values are: "json", "tsv", - and "excel". Default value is "json". - :paramtype file_format: str - :keyword asset_kind: Kind of the asset of the project. Known values are: "qnas" and "synonyms". - Default value is None. - :paramtype asset_kind: str - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of LROPoller that returns JSON object - :rtype: ~azure.core.polling.LROPoller[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - options = { - "assets": { - "qnas": [ - { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. - Question chosen as the head of suggested questions cluster by - Active Learning clustering algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": - 0, # Optional. The number of times the question was - suggested automatically by the Active Learning - algorithm. - "question": "str", # - Optional. Question suggested by the Active Learning - feature. - "userSuggestedCount": - 0 # Optional. The number of times the question was - suggested explicitly by the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark - if a prompt is relevant only with a previous question or not. If - true, do not include this QnA as answer for queries without - context; otherwise, ignores context and includes this QnA in - answers. - "prompts": [ - { - "displayOrder": 0, # - Optional. Index of the prompt. It is used for ordering of - the prompts. - "displayText": "str", # - Optional. Text displayed to represent a follow up - question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question - chosen as the head of suggested questions - cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. - The number of times the question was - suggested automatically by the Active - Learning algorithm. - "question": "str", # Optional. - Question suggested by the Active - Learning feature. - "userSuggestedCount": 0 # Optional. - The number of times the question was - suggested explicitly by the user. - } - ] - } - ], - "answer": "str", # - Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. - Unique ID for the QnA. - "metadata": { - "str": "str" - # Optional. Metadata associated with the answer, - useful to categorize or filter question answers. - }, - "questions": [ - "str" # - Optional. List of questions associated with the - answer. - ], - "source": "str" # - Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of - the QnA corresponding to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # - Optional. Date-time when the QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated - with the answer, useful to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str", # Optional. Source from which QnA - was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - "sourceDisplayName": "str" # Optional. Friendly name - of the Source. - } - ], - "synonyms": [ - { - "alterations": [ - "str" # Collection of word alterations. - Required. - ] - } - ] - }, - "fileUri": "str", # Optional. Import data File URI. - "metadata": { - "language": "str", # Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for - Spanish etc. If not set, use "en" for English as default. Required. - "description": "str", # Optional. Description of the project. - "multilingualResource": bool, # Optional. Set to true to enable - creating knowledgebases in different languages for the same resource. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response - when no good match is found in the knowledge base. - } - } - } - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Required. - "jobId": "str", # Required. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Required. - "status": "str", # Job Status. Required. Known values are: "notStarted", - "running", "succeeded", "failed", "cancelled", "cancelling", and - "partiallyCompleted". - "errors": [ - { - "code": "str", # One of a server-defined set of error codes. - Required. Known values are: "InvalidRequest", "InvalidArgument", - "Unauthorized", "Forbidden", "NotFound", "ProjectNotFound", - "OperationNotFound", "AzureCognitiveSearchNotFound", - "AzureCognitiveSearchIndexNotFound", "TooManyRequests", - "AzureCognitiveSearchThrottling", - "AzureCognitiveSearchIndexLimitReached", "InternalServerError", and - "ServiceUnavailable". - "message": "str", # A human-readable representation of the - error. Required. - "details": [ - ... - ], - "innererror": { - "code": "str", # One of a server-defined set of - error codes. Required. Known values are: "InvalidRequest", - "InvalidParameterValue", "KnowledgeBaseNotFound", - "AzureCognitiveSearchNotFound", "AzureCognitiveSearchThrottling", and - "ExtractionFailure". - "message": "str", # Error message. Required. - "details": { - "str": "str" # Optional. Error details. - }, - "innererror": ..., - "target": "str" # Optional. Error target. - }, - "target": "str" # Optional. The target of the error. - } - ], - "expirationDateTime": "2020-02-20 00:00:00" # Optional. - } - """ - - @overload - def begin_import_assets( - self, - project_name: str, - options: Optional[IO[bytes]] = None, - *, - file_format: str = "json", - asset_kind: Optional[str] = None, - content_type: str = "application/json", - **kwargs: Any - ) -> LROPoller[JSON]: - # pylint: disable=line-too-long - """Import project assets. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/import - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Project assets the needs to be imported. Default value is None. - :type options: IO[bytes] - :keyword file_format: Knowledge base Import or Export format. Known values are: "json", "tsv", - and "excel". Default value is "json". - :paramtype file_format: str - :keyword asset_kind: Kind of the asset of the project. Known values are: "qnas" and "synonyms". - Default value is None. - :paramtype asset_kind: str - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of LROPoller that returns JSON object - :rtype: ~azure.core.polling.LROPoller[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Required. - "jobId": "str", # Required. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Required. - "status": "str", # Job Status. Required. Known values are: "notStarted", - "running", "succeeded", "failed", "cancelled", "cancelling", and - "partiallyCompleted". - "errors": [ - { - "code": "str", # One of a server-defined set of error codes. - Required. Known values are: "InvalidRequest", "InvalidArgument", - "Unauthorized", "Forbidden", "NotFound", "ProjectNotFound", - "OperationNotFound", "AzureCognitiveSearchNotFound", - "AzureCognitiveSearchIndexNotFound", "TooManyRequests", - "AzureCognitiveSearchThrottling", - "AzureCognitiveSearchIndexLimitReached", "InternalServerError", and - "ServiceUnavailable". - "message": "str", # A human-readable representation of the - error. Required. - "details": [ - ... - ], - "innererror": { - "code": "str", # One of a server-defined set of - error codes. Required. Known values are: "InvalidRequest", - "InvalidParameterValue", "KnowledgeBaseNotFound", - "AzureCognitiveSearchNotFound", "AzureCognitiveSearchThrottling", and - "ExtractionFailure". - "message": "str", # Error message. Required. - "details": { - "str": "str" # Optional. Error details. - }, - "innererror": ..., - "target": "str" # Optional. Error target. - }, - "target": "str" # Optional. The target of the error. - } - ], - "expirationDateTime": "2020-02-20 00:00:00" # Optional. - } - """ - - @distributed_trace - def begin_import_assets( - self, - project_name: str, - options: Optional[Union[JSON, IO[bytes]]] = None, - *, - file_format: str = "json", - asset_kind: Optional[str] = None, - **kwargs: Any - ) -> LROPoller[JSON]: - # pylint: disable=line-too-long - """Import project assets. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/import - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Project assets the needs to be imported. Is either a JSON type or a IO[bytes] - type. Default value is None. - :type options: JSON or IO[bytes] - :keyword file_format: Knowledge base Import or Export format. Known values are: "json", "tsv", - and "excel". Default value is "json". - :paramtype file_format: str - :keyword asset_kind: Kind of the asset of the project. Known values are: "qnas" and "synonyms". - Default value is None. - :paramtype asset_kind: str - :return: An instance of LROPoller that returns JSON object - :rtype: ~azure.core.polling.LROPoller[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - options = { - "assets": { - "qnas": [ - { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. - Question chosen as the head of suggested questions cluster by - Active Learning clustering algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": - 0, # Optional. The number of times the question was - suggested automatically by the Active Learning - algorithm. - "question": "str", # - Optional. Question suggested by the Active Learning - feature. - "userSuggestedCount": - 0 # Optional. The number of times the question was - suggested explicitly by the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark - if a prompt is relevant only with a previous question or not. If - true, do not include this QnA as answer for queries without - context; otherwise, ignores context and includes this QnA in - answers. - "prompts": [ - { - "displayOrder": 0, # - Optional. Index of the prompt. It is used for ordering of - the prompts. - "displayText": "str", # - Optional. Text displayed to represent a follow up - question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question - chosen as the head of suggested questions - cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. - The number of times the question was - suggested automatically by the Active - Learning algorithm. - "question": "str", # Optional. - Question suggested by the Active - Learning feature. - "userSuggestedCount": 0 # Optional. - The number of times the question was - suggested explicitly by the user. - } - ] - } - ], - "answer": "str", # - Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. - Unique ID for the QnA. - "metadata": { - "str": "str" - # Optional. Metadata associated with the answer, - useful to categorize or filter question answers. - }, - "questions": [ - "str" # - Optional. List of questions associated with the - answer. - ], - "source": "str" # - Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of - the QnA corresponding to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # - Optional. Date-time when the QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated - with the answer, useful to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str", # Optional. Source from which QnA - was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - "sourceDisplayName": "str" # Optional. Friendly name - of the Source. - } - ], - "synonyms": [ - { - "alterations": [ - "str" # Collection of word alterations. - Required. - ] - } - ] - }, - "fileUri": "str", # Optional. Import data File URI. - "metadata": { - "language": "str", # Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for - Spanish etc. If not set, use "en" for English as default. Required. - "description": "str", # Optional. Description of the project. - "multilingualResource": bool, # Optional. Set to true to enable - creating knowledgebases in different languages for the same resource. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response - when no good match is found in the knowledge base. - } - } - } - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Required. - "jobId": "str", # Required. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Required. - "status": "str", # Job Status. Required. Known values are: "notStarted", - "running", "succeeded", "failed", "cancelled", "cancelling", and - "partiallyCompleted". - "errors": [ - { - "code": "str", # One of a server-defined set of error codes. - Required. Known values are: "InvalidRequest", "InvalidArgument", - "Unauthorized", "Forbidden", "NotFound", "ProjectNotFound", - "OperationNotFound", "AzureCognitiveSearchNotFound", - "AzureCognitiveSearchIndexNotFound", "TooManyRequests", - "AzureCognitiveSearchThrottling", - "AzureCognitiveSearchIndexLimitReached", "InternalServerError", and - "ServiceUnavailable". - "message": "str", # A human-readable representation of the - error. Required. - "details": [ - ... - ], - "innererror": { - "code": "str", # One of a server-defined set of - error codes. Required. Known values are: "InvalidRequest", - "InvalidParameterValue", "KnowledgeBaseNotFound", - "AzureCognitiveSearchNotFound", "AzureCognitiveSearchThrottling", and - "ExtractionFailure". - "message": "str", # Error message. Required. - "details": { - "str": "str" # Optional. Error details. - }, - "innererror": ..., - "target": "str" # Optional. Error target. - }, - "target": "str" # Optional. The target of the error. - } - ], - "expirationDateTime": "2020-02-20 00:00:00" # Optional. - } - """ - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[JSON] = kwargs.pop("cls", None) - polling: Union[bool, PollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = self._import_assets_initial( - project_name=project_name, - options=options, - file_format=file_format, - asset_kind=asset_kind, - content_type=content_type, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): - response = pipeline_response.http_response - if response.content: - deserialized = response.json() - else: - deserialized = None - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore - return deserialized - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: PollingMethod = cast( - PollingMethod, LROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) - ) - elif polling is False: - polling_method = cast(PollingMethod, NoPolling()) - else: - polling_method = polling - if cont_token: - return LROPoller[JSON].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return LROPoller[JSON](self._client, raw_result, get_long_running_output, polling_method) # type: ignore - - def _deploy_project_initial(self, project_name: str, deployment_name: str, **kwargs: Any) -> Optional[JSON]: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) - - _request = build_authoring_deploy_project_request( - project_name=project_name, - deployment_name=deployment_name, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 202]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - deserialized = None - response_headers = {} - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 202: - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) - - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore - - @distributed_trace - def begin_deploy_project(self, project_name: str, deployment_name: str, **kwargs: Any) -> LROPoller[JSON]: - """Deploy project to production. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/deploy-project - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param deployment_name: The name of the specific deployment of the project to use. Required. - :type deployment_name: str - :return: An instance of LROPoller that returns JSON object - :rtype: ~azure.core.polling.LROPoller[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "deploymentName": "str", # Optional. Name of the deployment. - "lastDeployedDateTime": "2020-02-20 00:00:00" # Optional. Represents the - project last deployment date-time. - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[JSON] = kwargs.pop("cls", None) - polling: Union[bool, PollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = self._deploy_project_initial( - project_name=project_name, - deployment_name=deployment_name, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): - response = pipeline_response.http_response - if response.content: - deserialized = response.json() - else: - deserialized = None - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore - return deserialized - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: PollingMethod = cast( - PollingMethod, LROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) - ) - elif polling is False: - polling_method = cast(PollingMethod, NoPolling()) - else: - polling_method = polling - if cont_token: - return LROPoller[JSON].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return LROPoller[JSON](self._client, raw_result, get_long_running_output, polling_method) # type: ignore - - @distributed_trace - def list_deployments( - self, project_name: str, *, top: Optional[int] = None, skip: Optional[int] = None, **kwargs: Any - ) -> Iterable[JSON]: - """List all deployments of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/list-deployments - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :keyword top: The maximum number of resources to return from the collection. Default value is - None. - :paramtype top: int - :keyword skip: An offset into the collection of the first resource to be returned. Default - value is None. - :paramtype skip: int - :return: An iterator like instance of JSON object - :rtype: ~azure.core.paging.ItemPaged[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "deploymentName": "str", # Optional. Name of the deployment. - "lastDeployedDateTime": "2020-02-20 00:00:00" # Optional. Represents the - project last deployment date-time. - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - maxpagesize = kwargs.pop("maxpagesize", None) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_list_deployments_request( - project_name=project_name, - top=top, - skip=skip, - maxpagesize=maxpagesize, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) - - def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return ItemPaged(get_next, extract_data) - - @distributed_trace - def list_synonyms( - self, project_name: str, *, top: Optional[int] = None, skip: Optional[int] = None, **kwargs: Any - ) -> Iterable[JSON]: - """Gets all the synonyms of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/get-synonyms - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :keyword top: The maximum number of resources to return from the collection. Default value is - None. - :paramtype top: int - :keyword skip: An offset into the collection of the first resource to be returned. Default - value is None. - :paramtype skip: int - :return: An iterator like instance of JSON object - :rtype: ~azure.core.paging.ItemPaged[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "alterations": [ - "str" # Collection of word alterations. Required. - ] - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - maxpagesize = kwargs.pop("maxpagesize", None) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_list_synonyms_request( - project_name=project_name, - top=top, - skip=skip, - maxpagesize=maxpagesize, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) - - def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return ItemPaged(get_next, extract_data) - - @overload - def update_synonyms( # pylint: disable=inconsistent-return-statements - self, project_name: str, synonyms: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> None: - """Updates all the synonyms of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-synonyms - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param synonyms: All the synonyms of a project. Required. - :type synonyms: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - synonyms = { - "nextLink": "str", # Optional. - "value": [ - { - "alterations": [ - "str" # Collection of word alterations. Required. - ] - } - ] - } - """ - - @overload - def update_synonyms( # pylint: disable=inconsistent-return-statements - self, project_name: str, synonyms: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> None: - """Updates all the synonyms of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-synonyms - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param synonyms: All the synonyms of a project. Required. - :type synonyms: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @distributed_trace - def update_synonyms( # pylint: disable=inconsistent-return-statements - self, project_name: str, synonyms: Union[JSON, IO[bytes]], **kwargs: Any - ) -> None: - """Updates all the synonyms of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-synonyms - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param synonyms: All the synonyms of a project. Is either a JSON type or a IO[bytes] type. - Required. - :type synonyms: JSON or IO[bytes] - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - synonyms = { - "nextLink": "str", # Optional. - "value": [ - { - "alterations": [ - "str" # Collection of word alterations. Required. - ] - } - ] - } - """ - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[None] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(synonyms, (IOBase, bytes)): - _content = synonyms - else: - _json = synonyms - - _request = build_authoring_update_synonyms_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [204]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if cls: - return cls(pipeline_response, None, {}) # type: ignore - - @distributed_trace - def list_sources( - self, project_name: str, *, top: Optional[int] = None, skip: Optional[int] = None, **kwargs: Any - ) -> Iterable[JSON]: - # pylint: disable=line-too-long - """Gets all the sources of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/get-sources - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :keyword top: The maximum number of resources to return from the collection. Default value is - None. - :paramtype top: int - :keyword skip: An offset into the collection of the first resource to be returned. Default - value is None. - :paramtype skip: int - :return: An iterator like instance of JSON object - :rtype: ~azure.core.paging.ItemPaged[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "sourceKind": "str", # Supported source types. Required. Known values are: - "file" and "url". - "sourceUri": "str", # URI location for the file or url. Required. - "contentStructureKind": "str", # Optional. Content structure type for - sources. "unstructured" - "displayName": "str", # Optional. Friendly name of the Source. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "source": "str" # Optional. Unique source identifier. Name of the file if - it's a 'file' source; otherwise, the complete URL if it's a 'url' source. - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - maxpagesize = kwargs.pop("maxpagesize", None) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_list_sources_request( - project_name=project_name, - top=top, - skip=skip, - maxpagesize=maxpagesize, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) - - def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return ItemPaged(get_next, extract_data) - - def _update_sources_initial( - self, project_name: str, sources: Union[List[JSON], IO[bytes]], **kwargs: Any - ) -> Optional[JSON]: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(sources, (IOBase, bytes)): - _content = sources - else: - _json = sources - - _request = build_authoring_update_sources_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 202]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - deserialized = None - response_headers = {} - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 202: - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) - - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore - - @overload - def begin_update_sources( - self, project_name: str, sources: List[JSON], *, content_type: str = "application/json", **kwargs: Any - ) -> LROPoller[Iterable[JSON]]: - # pylint: disable=line-too-long - """Updates the sources of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-sources - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param sources: Update sources parameters of a project. Required. - :type sources: list[JSON] - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.LROPoller[~azure.core.paging.ItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - sources = [ - { - "op": "str", # Update operation type for assets. Required. Known - values are: "add", "delete", and "replace". - "value": { - "sourceKind": "str", # Supported source types. Required. - Known values are: "file" and "url". - "sourceUri": "str", # URI location for the file or url. - Required. - "contentStructureKind": "str", # Optional. Content structure - type for sources. "unstructured" - "displayName": "str", # Optional. Friendly name of the - Source. - "refresh": bool, # Optional. Boolean flag used to refresh - data from the Source. - "source": "str" # Optional. Unique source identifier. Name - of the file if it's a 'file' source; otherwise, the complete URL if it's - a 'url' source. - } - } - ] - - # response body for status code(s): 200, 202 - response == { - "sourceKind": "str", # Supported source types. Required. Known values are: - "file" and "url". - "sourceUri": "str", # URI location for the file or url. Required. - "contentStructureKind": "str", # Optional. Content structure type for - sources. "unstructured" - "displayName": "str", # Optional. Friendly name of the Source. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "source": "str" # Optional. Unique source identifier. Name of the file if - it's a 'file' source; otherwise, the complete URL if it's a 'url' source. - } - """ - - @overload - def begin_update_sources( - self, project_name: str, sources: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> LROPoller[Iterable[JSON]]: - # pylint: disable=line-too-long - """Updates the sources of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-sources - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param sources: Update sources parameters of a project. Required. - :type sources: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.LROPoller[~azure.core.paging.ItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200, 202 - response == { - "sourceKind": "str", # Supported source types. Required. Known values are: - "file" and "url". - "sourceUri": "str", # URI location for the file or url. Required. - "contentStructureKind": "str", # Optional. Content structure type for - sources. "unstructured" - "displayName": "str", # Optional. Friendly name of the Source. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "source": "str" # Optional. Unique source identifier. Name of the file if - it's a 'file' source; otherwise, the complete URL if it's a 'url' source. - } - """ - - @distributed_trace - def begin_update_sources( - self, project_name: str, sources: Union[List[JSON], IO[bytes]], **kwargs: Any - ) -> LROPoller[Iterable[JSON]]: - # pylint: disable=line-too-long - """Updates the sources of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-sources - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param sources: Update sources parameters of a project. Is either a [JSON] type or a IO[bytes] - type. Required. - :type sources: list[JSON] or IO[bytes] - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.LROPoller[~azure.core.paging.ItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200, 202 - response == { - "sourceKind": "str", # Supported source types. Required. Known values are: - "file" and "url". - "sourceUri": "str", # URI location for the file or url. Required. - "contentStructureKind": "str", # Optional. Content structure type for - sources. "unstructured" - "displayName": "str", # Optional. Friendly name of the Source. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "source": "str" # Optional. Unique source identifier. Name of the file if - it's a 'file' source; otherwise, the complete URL if it's a 'url' source. - } - """ - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(sources, (IOBase, bytes)): - _content = sources - else: - _json = sources - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_update_sources_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) - - def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - polling: Union[bool, PollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = self._update_sources_initial( - project_name=project_name, - sources=sources, - content_type=content_type, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): - def internal_get_next(next_link=None): - if next_link is None: - return pipeline_response - return get_next(next_link) - - return ItemPaged(internal_get_next, extract_data) - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: PollingMethod = cast( - PollingMethod, LROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) - ) - elif polling is False: - polling_method = cast(PollingMethod, NoPolling()) - else: - polling_method = polling - if cont_token: - return LROPoller[Iterable[JSON]].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return LROPoller[Iterable[JSON]]( - self._client, raw_result, get_long_running_output, polling_method # type: ignore - ) - - @distributed_trace - def list_qnas( - self, - project_name: str, - *, - source: Optional[str] = None, - top: Optional[int] = None, - skip: Optional[int] = None, - **kwargs: Any - ) -> Iterable[JSON]: - # pylint: disable=line-too-long - """Gets all the QnAs of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/get-qnas - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :keyword source: Source of the QnA. Default value is None. - :paramtype source: str - :keyword top: The maximum number of resources to return from the collection. Default value is - None. - :paramtype top: int - :keyword skip: An offset into the collection of the first resource to be returned. Default - value is None. - :paramtype skip: int - :return: An iterator like instance of JSON object - :rtype: ~azure.core.paging.ItemPaged[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question chosen as the - head of suggested questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The - number of times the question was suggested automatically by the - Active Learning algorithm. - "question": "str", # Optional. Question - suggested by the Active Learning feature. - "userSuggestedCount": 0 # Optional. The - number of times the question was suggested explicitly by the - user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark if a prompt is relevant - only with a previous question or not. If true, do not include this QnA as - answer for queries without context; otherwise, ignores context and includes - this QnA in answers. - "prompts": [ - { - "displayOrder": 0, # Optional. Index of the prompt. - It is used for ordering of the prompts. - "displayText": "str", # Optional. Text displayed to - represent a follow up question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # - Optional. Question chosen as the head of suggested - questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The number - of times the question was suggested automatically - by the Active Learning algorithm. - "question": - "str", # Optional. Question suggested by the - Active Learning feature. - "userSuggestedCount": 0 # Optional. The number - of times the question was suggested explicitly by - the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. Unique ID for the QnA. - "metadata": { - "str": "str" # Optional. Metadata - associated with the answer, useful to categorize or filter - question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str" # Optional. Source from - which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of the QnA corresponding - to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated with the answer, useful - to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions associated with the answer. - ], - "source": "str" # Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs . - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - maxpagesize = kwargs.pop("maxpagesize", None) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_list_qnas_request( - project_name=project_name, - source=source, - top=top, - skip=skip, - maxpagesize=maxpagesize, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) - - def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return ItemPaged(get_next, extract_data) - - def _update_qnas_initial( - self, project_name: str, qnas: Union[List[JSON], IO[bytes]], **kwargs: Any - ) -> Optional[JSON]: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(qnas, (IOBase, bytes)): - _content = qnas - else: - _json = qnas - - _request = build_authoring_update_qnas_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 202]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - deserialized = None - response_headers = {} - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 202: - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) - - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore - - @overload - def begin_update_qnas( - self, project_name: str, qnas: List[JSON], *, content_type: str = "application/json", **kwargs: Any - ) -> LROPoller[Iterable[JSON]]: - # pylint: disable=line-too-long - """Updates the QnAs of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-qnas - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param qnas: Update QnAs parameters of a project. Required. - :type qnas: list[JSON] - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.LROPoller[~azure.core.paging.ItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - qnas = [ - { - "op": "str", # Update operation type for assets. Required. Known - values are: "add", "delete", and "replace". - "value": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question - chosen as the head of suggested questions cluster by Active - Learning clustering algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # - Optional. The number of times the question was suggested - automatically by the Active Learning algorithm. - "question": "str", # - Optional. Question suggested by the Active Learning - feature. - "userSuggestedCount": 0 # - Optional. The number of times the question was suggested - explicitly by the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark if a - prompt is relevant only with a previous question or not. If true, do - not include this QnA as answer for queries without context; - otherwise, ignores context and includes this QnA in answers. - "prompts": [ - { - "displayOrder": 0, # Optional. Index - of the prompt. It is used for ordering of the prompts. - "displayText": "str", # Optional. - Text displayed to represent a follow up question prompt. - "qna": ..., - "qnaId": 0 # Optional. ID of the QnA - corresponding to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "metadata": { - "str": "str" # Optional. Metadata associated with - the answer, useful to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions associated with - the answer. - ], - "source": "str" # Optional. Source from which QnA was - indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs . - } - } - ] - - # response body for status code(s): 200, 202 - response == { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question chosen as the - head of suggested questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The - number of times the question was suggested automatically by the - Active Learning algorithm. - "question": "str", # Optional. Question - suggested by the Active Learning feature. - "userSuggestedCount": 0 # Optional. The - number of times the question was suggested explicitly by the - user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark if a prompt is relevant - only with a previous question or not. If true, do not include this QnA as - answer for queries without context; otherwise, ignores context and includes - this QnA in answers. - "prompts": [ - { - "displayOrder": 0, # Optional. Index of the prompt. - It is used for ordering of the prompts. - "displayText": "str", # Optional. Text displayed to - represent a follow up question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # - Optional. Question chosen as the head of suggested - questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The number - of times the question was suggested automatically - by the Active Learning algorithm. - "question": - "str", # Optional. Question suggested by the - Active Learning feature. - "userSuggestedCount": 0 # Optional. The number - of times the question was suggested explicitly by - the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. Unique ID for the QnA. - "metadata": { - "str": "str" # Optional. Metadata - associated with the answer, useful to categorize or filter - question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str" # Optional. Source from - which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of the QnA corresponding - to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated with the answer, useful - to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions associated with the answer. - ], - "source": "str" # Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs . - } - """ - - @overload - def begin_update_qnas( - self, project_name: str, qnas: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> LROPoller[Iterable[JSON]]: - # pylint: disable=line-too-long - """Updates the QnAs of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-qnas - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param qnas: Update QnAs parameters of a project. Required. - :type qnas: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.LROPoller[~azure.core.paging.ItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200, 202 - response == { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question chosen as the - head of suggested questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The - number of times the question was suggested automatically by the - Active Learning algorithm. - "question": "str", # Optional. Question - suggested by the Active Learning feature. - "userSuggestedCount": 0 # Optional. The - number of times the question was suggested explicitly by the - user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark if a prompt is relevant - only with a previous question or not. If true, do not include this QnA as - answer for queries without context; otherwise, ignores context and includes - this QnA in answers. - "prompts": [ - { - "displayOrder": 0, # Optional. Index of the prompt. - It is used for ordering of the prompts. - "displayText": "str", # Optional. Text displayed to - represent a follow up question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # - Optional. Question chosen as the head of suggested - questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The number - of times the question was suggested automatically - by the Active Learning algorithm. - "question": - "str", # Optional. Question suggested by the - Active Learning feature. - "userSuggestedCount": 0 # Optional. The number - of times the question was suggested explicitly by - the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. Unique ID for the QnA. - "metadata": { - "str": "str" # Optional. Metadata - associated with the answer, useful to categorize or filter - question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str" # Optional. Source from - which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of the QnA corresponding - to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated with the answer, useful - to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions associated with the answer. - ], - "source": "str" # Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs . - } - """ - - @distributed_trace - def begin_update_qnas( - self, project_name: str, qnas: Union[List[JSON], IO[bytes]], **kwargs: Any - ) -> LROPoller[Iterable[JSON]]: - # pylint: disable=line-too-long - """Updates the QnAs of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-qnas - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param qnas: Update QnAs parameters of a project. Is either a [JSON] type or a IO[bytes] type. - Required. - :type qnas: list[JSON] or IO[bytes] - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.LROPoller[~azure.core.paging.ItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200, 202 - response == { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question chosen as the - head of suggested questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The - number of times the question was suggested automatically by the - Active Learning algorithm. - "question": "str", # Optional. Question - suggested by the Active Learning feature. - "userSuggestedCount": 0 # Optional. The - number of times the question was suggested explicitly by the - user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark if a prompt is relevant - only with a previous question or not. If true, do not include this QnA as - answer for queries without context; otherwise, ignores context and includes - this QnA in answers. - "prompts": [ - { - "displayOrder": 0, # Optional. Index of the prompt. - It is used for ordering of the prompts. - "displayText": "str", # Optional. Text displayed to - represent a follow up question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # - Optional. Question chosen as the head of suggested - questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The number - of times the question was suggested automatically - by the Active Learning algorithm. - "question": - "str", # Optional. Question suggested by the - Active Learning feature. - "userSuggestedCount": 0 # Optional. The number - of times the question was suggested explicitly by - the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. Unique ID for the QnA. - "metadata": { - "str": "str" # Optional. Metadata - associated with the answer, useful to categorize or filter - question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str" # Optional. Source from - which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of the QnA corresponding - to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated with the answer, useful - to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions associated with the answer. - ], - "source": "str" # Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs . - } - """ - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(qnas, (IOBase, bytes)): - _content = qnas - else: - _json = qnas - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_update_qnas_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, iter(list_of_elem) - - def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - polling: Union[bool, PollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = self._update_qnas_initial( - project_name=project_name, - qnas=qnas, - content_type=content_type, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): - def internal_get_next(next_link=None): - if next_link is None: - return pipeline_response - return get_next(next_link) - - return ItemPaged(internal_get_next, extract_data) - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: PollingMethod = cast( - PollingMethod, LROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs) - ) - elif polling is False: - polling_method = cast(PollingMethod, NoPolling()) - else: - polling_method = polling - if cont_token: - return LROPoller[Iterable[JSON]].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return LROPoller[Iterable[JSON]]( - self._client, raw_result, get_long_running_output, polling_method # type: ignore - ) - - @overload - def add_feedback( # pylint: disable=inconsistent-return-statements - self, project_name: str, feedback: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> None: - """Update Active Learning feedback. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/add-feedback - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param feedback: Feedback for Active Learning. Required. - :type feedback: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - feedback = { - "records": [ - { - "qnaId": 0, # Optional. Unique ID of the QnA. - "userId": "str", # Optional. Unique identifier of the user. - "userQuestion": "str" # Optional. User suggested question - for the QnA. - } - ] - } - """ - - @overload - def add_feedback( # pylint: disable=inconsistent-return-statements - self, project_name: str, feedback: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> None: - """Update Active Learning feedback. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/add-feedback - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param feedback: Feedback for Active Learning. Required. - :type feedback: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @distributed_trace - def add_feedback( # pylint: disable=inconsistent-return-statements - self, project_name: str, feedback: Union[JSON, IO[bytes]], **kwargs: Any - ) -> None: - """Update Active Learning feedback. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/add-feedback - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param feedback: Feedback for Active Learning. Is either a JSON type or a IO[bytes] type. - Required. - :type feedback: JSON or IO[bytes] - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - feedback = { - "records": [ - { - "qnaId": 0, # Optional. Unique ID of the QnA. - "userId": "str", # Optional. Unique identifier of the user. - "userQuestion": "str" # Optional. User suggested question - for the QnA. - } - ] - } - """ - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[None] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(feedback, (IOBase, bytes)): - _content = feedback - else: - _json = feedback - - _request = build_authoring_add_feedback_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [204]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if cls: - return cls(pipeline_response, None, {}) # type: ignore diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_operations/_patch.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_operations/_patch.py deleted file mode 100644 index f7dd32510333..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_operations/_patch.py +++ /dev/null @@ -1,20 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -"""Customize generated code here. - -Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize -""" -from typing import List - -__all__: List[str] = [] # Add all objects you want publicly available to users at this package level - - -def patch_sdk(): - """Do not remove from this file. - - `patch_sdk` is a last resort escape hatch that allows you to do customizations - you can't accomplish using the techniques described in - https://aka.ms/azsdk/python/dpcodegen/python/customize - """ diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_patch.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_patch.py deleted file mode 100644 index d92745dcb197..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_patch.py +++ /dev/null @@ -1,80 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -"""Customize generated code here. - -Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize -""" -from typing import List, Union, Any -from azure.core.credentials import AzureKeyCredential, TokenCredential -from azure.core.pipeline.policies import AzureKeyCredentialPolicy, BearerTokenCredentialPolicy -from ._client import AuthoringClient as AuthoringClientGenerated - -POLLING_INTERVAL_DEFAULT = 5 - - -def _authentication_policy(credential, **kwargs): - if credential is None: - raise ValueError("Parameter 'credential' must not be None.") - if isinstance(credential, AzureKeyCredential): - authentication_policy = AzureKeyCredentialPolicy( - name="Ocp-Apim-Subscription-Key", credential=credential, **kwargs - ) - elif hasattr(credential, "get_token"): - authentication_policy = BearerTokenCredentialPolicy( - credential, *kwargs.pop("credential_scopes", ["https://cognitiveservices.azure.com/.default"]), **kwargs - ) - else: - raise TypeError( - "Unsupported credential: {}. Use an instance of AzureKeyCredential " - "or a token credential from azure.identity".format(type(credential)) - ) - return authentication_policy - - -class AuthoringClient(AuthoringClientGenerated): - """The language service API is a suite of natural language processing (NLP) skills built with - best-in-class Microsoft machine learning algorithms. The API can be used to analyze - unstructured text for tasks such as sentiment analysis, key phrase extraction, language - detection and question answering. Further documentation can be found in - https://learn.microsoft.com/azure/cognitive-services/language-service/overview - - :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.cognitiveservices.azure.com). - :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. - This can be the an instance of AzureKeyCredential if using a Language API key - or a token credential from :mod:`azure.identity`. - :type credential: ~azure.core.credentials.AzureKeyCredential or ~azure.core.credentials.TokenCredential - :keyword api_version: Api Version. The default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. - :paramtype api_version: str - :keyword int polling_interval: Default waiting time between two polls for LRO operations if no - Retry-After header is present. - """ - - def __init__(self, endpoint: str, credential: Union[AzureKeyCredential, TokenCredential], **kwargs: Any) -> None: - try: - endpoint = endpoint.rstrip("/") - except AttributeError as exc: - raise ValueError("Parameter 'endpoint' must be a string.") from exc - super().__init__( - endpoint=endpoint, - credential=credential, # type: ignore - authentication_policy=kwargs.pop("authentication_policy", _authentication_policy(credential, **kwargs)), - polling_interval=kwargs.pop("polling_interval", POLLING_INTERVAL_DEFAULT), - **kwargs - ) - - -__all__: List[str] = ["AuthoringClient"] # Add all objects you want publicly available to users at this package level - - -def patch_sdk(): - """Do not remove from this file. - - `patch_sdk` is a last resort escape hatch that allows you to do customizations - you can't accomplish using the techniques described in - https://aka.ms/azsdk/python/dpcodegen/python/customize - """ diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_serialization.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_serialization.py deleted file mode 100644 index 2f781d740827..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/_serialization.py +++ /dev/null @@ -1,1998 +0,0 @@ -# -------------------------------------------------------------------------- -# -# Copyright (c) Microsoft Corporation. All rights reserved. -# -# The MIT License (MIT) -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the ""Software""), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. -# -# -------------------------------------------------------------------------- - -# pylint: skip-file -# pyright: reportUnnecessaryTypeIgnoreComment=false - -from base64 import b64decode, b64encode -import calendar -import datetime -import decimal -import email -from enum import Enum -import json -import logging -import re -import sys -import codecs -from typing import ( - Dict, - Any, - cast, - Optional, - Union, - AnyStr, - IO, - Mapping, - Callable, - TypeVar, - MutableMapping, - Type, - List, - Mapping, -) - -try: - from urllib import quote # type: ignore -except ImportError: - from urllib.parse import quote -import xml.etree.ElementTree as ET - -import isodate # type: ignore - -from azure.core.exceptions import DeserializationError, SerializationError -from azure.core.serialization import NULL as CoreNull - -_BOM = codecs.BOM_UTF8.decode(encoding="utf-8") - -ModelType = TypeVar("ModelType", bound="Model") -JSON = MutableMapping[str, Any] - - -class RawDeserializer: - - # Accept "text" because we're open minded people... - JSON_REGEXP = re.compile(r"^(application|text)/([a-z+.]+\+)?json$") - - # Name used in context - CONTEXT_NAME = "deserialized_data" - - @classmethod - def deserialize_from_text(cls, data: Optional[Union[AnyStr, IO]], content_type: Optional[str] = None) -> Any: - """Decode data according to content-type. - - Accept a stream of data as well, but will be load at once in memory for now. - - If no content-type, will return the string version (not bytes, not stream) - - :param data: Input, could be bytes or stream (will be decoded with UTF8) or text - :type data: str or bytes or IO - :param str content_type: The content type. - """ - if hasattr(data, "read"): - # Assume a stream - data = cast(IO, data).read() - - if isinstance(data, bytes): - data_as_str = data.decode(encoding="utf-8-sig") - else: - # Explain to mypy the correct type. - data_as_str = cast(str, data) - - # Remove Byte Order Mark if present in string - data_as_str = data_as_str.lstrip(_BOM) - - if content_type is None: - return data - - if cls.JSON_REGEXP.match(content_type): - try: - return json.loads(data_as_str) - except ValueError as err: - raise DeserializationError("JSON is invalid: {}".format(err), err) - elif "xml" in (content_type or []): - try: - - try: - if isinstance(data, unicode): # type: ignore - # If I'm Python 2.7 and unicode XML will scream if I try a "fromstring" on unicode string - data_as_str = data_as_str.encode(encoding="utf-8") # type: ignore - except NameError: - pass - - return ET.fromstring(data_as_str) # nosec - except ET.ParseError as err: - # It might be because the server has an issue, and returned JSON with - # content-type XML.... - # So let's try a JSON load, and if it's still broken - # let's flow the initial exception - def _json_attemp(data): - try: - return True, json.loads(data) - except ValueError: - return False, None # Don't care about this one - - success, json_result = _json_attemp(data) - if success: - return json_result - # If i'm here, it's not JSON, it's not XML, let's scream - # and raise the last context in this block (the XML exception) - # The function hack is because Py2.7 messes up with exception - # context otherwise. - _LOGGER.critical("Wasn't XML not JSON, failing") - raise DeserializationError("XML is invalid") from err - raise DeserializationError("Cannot deserialize content-type: {}".format(content_type)) - - @classmethod - def deserialize_from_http_generics(cls, body_bytes: Optional[Union[AnyStr, IO]], headers: Mapping) -> Any: - """Deserialize from HTTP response. - - Use bytes and headers to NOT use any requests/aiohttp or whatever - specific implementation. - Headers will tested for "content-type" - """ - # Try to use content-type from headers if available - content_type = None - if "content-type" in headers: - content_type = headers["content-type"].split(";")[0].strip().lower() - # Ouch, this server did not declare what it sent... - # Let's guess it's JSON... - # Also, since Autorest was considering that an empty body was a valid JSON, - # need that test as well.... - else: - content_type = "application/json" - - if body_bytes: - return cls.deserialize_from_text(body_bytes, content_type) - return None - - -_LOGGER = logging.getLogger(__name__) - -try: - _long_type = long # type: ignore -except NameError: - _long_type = int - - -class UTC(datetime.tzinfo): - """Time Zone info for handling UTC""" - - def utcoffset(self, dt): - """UTF offset for UTC is 0.""" - return datetime.timedelta(0) - - def tzname(self, dt): - """Timestamp representation.""" - return "Z" - - def dst(self, dt): - """No daylight saving for UTC.""" - return datetime.timedelta(hours=1) - - -try: - from datetime import timezone as _FixedOffset # type: ignore -except ImportError: # Python 2.7 - - class _FixedOffset(datetime.tzinfo): # type: ignore - """Fixed offset in minutes east from UTC. - Copy/pasted from Python doc - :param datetime.timedelta offset: offset in timedelta format - """ - - def __init__(self, offset): - self.__offset = offset - - def utcoffset(self, dt): - return self.__offset - - def tzname(self, dt): - return str(self.__offset.total_seconds() / 3600) - - def __repr__(self): - return "".format(self.tzname(None)) - - def dst(self, dt): - return datetime.timedelta(0) - - def __getinitargs__(self): - return (self.__offset,) - - -try: - from datetime import timezone - - TZ_UTC = timezone.utc -except ImportError: - TZ_UTC = UTC() # type: ignore - -_FLATTEN = re.compile(r"(? None: - self.additional_properties: Optional[Dict[str, Any]] = {} - for k in kwargs: - if k not in self._attribute_map: - _LOGGER.warning("%s is not a known attribute of class %s and will be ignored", k, self.__class__) - elif k in self._validation and self._validation[k].get("readonly", False): - _LOGGER.warning("Readonly attribute %s will be ignored in class %s", k, self.__class__) - else: - setattr(self, k, kwargs[k]) - - def __eq__(self, other: Any) -> bool: - """Compare objects by comparing all attributes.""" - if isinstance(other, self.__class__): - return self.__dict__ == other.__dict__ - return False - - def __ne__(self, other: Any) -> bool: - """Compare objects by comparing all attributes.""" - return not self.__eq__(other) - - def __str__(self) -> str: - return str(self.__dict__) - - @classmethod - def enable_additional_properties_sending(cls) -> None: - cls._attribute_map["additional_properties"] = {"key": "", "type": "{object}"} - - @classmethod - def is_xml_model(cls) -> bool: - try: - cls._xml_map # type: ignore - except AttributeError: - return False - return True - - @classmethod - def _create_xml_node(cls): - """Create XML node.""" - try: - xml_map = cls._xml_map # type: ignore - except AttributeError: - xml_map = {} - - return _create_xml_node(xml_map.get("name", cls.__name__), xml_map.get("prefix", None), xml_map.get("ns", None)) - - def serialize(self, keep_readonly: bool = False, **kwargs: Any) -> JSON: - """Return the JSON that would be sent to server from this model. - - This is an alias to `as_dict(full_restapi_key_transformer, keep_readonly=False)`. - - If you want XML serialization, you can pass the kwargs is_xml=True. - - :param bool keep_readonly: If you want to serialize the readonly attributes - :returns: A dict JSON compatible object - :rtype: dict - """ - serializer = Serializer(self._infer_class_models()) - return serializer._serialize(self, keep_readonly=keep_readonly, **kwargs) # type: ignore - - def as_dict( - self, - keep_readonly: bool = True, - key_transformer: Callable[[str, Dict[str, Any], Any], Any] = attribute_transformer, - **kwargs: Any - ) -> JSON: - """Return a dict that can be serialized using json.dump. - - Advanced usage might optionally use a callback as parameter: - - .. code::python - - def my_key_transformer(key, attr_desc, value): - return key - - Key is the attribute name used in Python. Attr_desc - is a dict of metadata. Currently contains 'type' with the - msrest type and 'key' with the RestAPI encoded key. - Value is the current value in this object. - - The string returned will be used to serialize the key. - If the return type is a list, this is considered hierarchical - result dict. - - See the three examples in this file: - - - attribute_transformer - - full_restapi_key_transformer - - last_restapi_key_transformer - - If you want XML serialization, you can pass the kwargs is_xml=True. - - :param function key_transformer: A key transformer function. - :returns: A dict JSON compatible object - :rtype: dict - """ - serializer = Serializer(self._infer_class_models()) - return serializer._serialize(self, key_transformer=key_transformer, keep_readonly=keep_readonly, **kwargs) # type: ignore - - @classmethod - def _infer_class_models(cls): - try: - str_models = cls.__module__.rsplit(".", 1)[0] - models = sys.modules[str_models] - client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} - if cls.__name__ not in client_models: - raise ValueError("Not Autorest generated code") - except Exception: - # Assume it's not Autorest generated (tests?). Add ourselves as dependencies. - client_models = {cls.__name__: cls} - return client_models - - @classmethod - def deserialize(cls: Type[ModelType], data: Any, content_type: Optional[str] = None) -> ModelType: - """Parse a str using the RestAPI syntax and return a model. - - :param str data: A str using RestAPI structure. JSON by default. - :param str content_type: JSON by default, set application/xml if XML. - :returns: An instance of this model - :raises: DeserializationError if something went wrong - """ - deserializer = Deserializer(cls._infer_class_models()) - return deserializer(cls.__name__, data, content_type=content_type) # type: ignore - - @classmethod - def from_dict( - cls: Type[ModelType], - data: Any, - key_extractors: Optional[Callable[[str, Dict[str, Any], Any], Any]] = None, - content_type: Optional[str] = None, - ) -> ModelType: - """Parse a dict using given key extractor return a model. - - By default consider key - extractors (rest_key_case_insensitive_extractor, attribute_key_case_insensitive_extractor - and last_rest_key_case_insensitive_extractor) - - :param dict data: A dict using RestAPI structure - :param str content_type: JSON by default, set application/xml if XML. - :returns: An instance of this model - :raises: DeserializationError if something went wrong - """ - deserializer = Deserializer(cls._infer_class_models()) - deserializer.key_extractors = ( # type: ignore - [ # type: ignore - attribute_key_case_insensitive_extractor, - rest_key_case_insensitive_extractor, - last_rest_key_case_insensitive_extractor, - ] - if key_extractors is None - else key_extractors - ) - return deserializer(cls.__name__, data, content_type=content_type) # type: ignore - - @classmethod - def _flatten_subtype(cls, key, objects): - if "_subtype_map" not in cls.__dict__: - return {} - result = dict(cls._subtype_map[key]) - for valuetype in cls._subtype_map[key].values(): - result.update(objects[valuetype]._flatten_subtype(key, objects)) - return result - - @classmethod - def _classify(cls, response, objects): - """Check the class _subtype_map for any child classes. - We want to ignore any inherited _subtype_maps. - Remove the polymorphic key from the initial data. - """ - for subtype_key in cls.__dict__.get("_subtype_map", {}).keys(): - subtype_value = None - - if not isinstance(response, ET.Element): - rest_api_response_key = cls._get_rest_key_parts(subtype_key)[-1] - subtype_value = response.pop(rest_api_response_key, None) or response.pop(subtype_key, None) - else: - subtype_value = xml_key_extractor(subtype_key, cls._attribute_map[subtype_key], response) - if subtype_value: - # Try to match base class. Can be class name only - # (bug to fix in Autorest to support x-ms-discriminator-name) - if cls.__name__ == subtype_value: - return cls - flatten_mapping_type = cls._flatten_subtype(subtype_key, objects) - try: - return objects[flatten_mapping_type[subtype_value]] # type: ignore - except KeyError: - _LOGGER.warning( - "Subtype value %s has no mapping, use base class %s.", - subtype_value, - cls.__name__, - ) - break - else: - _LOGGER.warning("Discriminator %s is absent or null, use base class %s.", subtype_key, cls.__name__) - break - return cls - - @classmethod - def _get_rest_key_parts(cls, attr_key): - """Get the RestAPI key of this attr, split it and decode part - :param str attr_key: Attribute key must be in attribute_map. - :returns: A list of RestAPI part - :rtype: list - """ - rest_split_key = _FLATTEN.split(cls._attribute_map[attr_key]["key"]) - return [_decode_attribute_map_key(key_part) for key_part in rest_split_key] - - -def _decode_attribute_map_key(key): - """This decode a key in an _attribute_map to the actual key we want to look at - inside the received data. - - :param str key: A key string from the generated code - """ - return key.replace("\\.", ".") - - -class Serializer(object): - """Request object model serializer.""" - - basic_types = {str: "str", int: "int", bool: "bool", float: "float"} - - _xml_basic_types_serializers = {"bool": lambda x: str(x).lower()} - days = {0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu", 4: "Fri", 5: "Sat", 6: "Sun"} - months = { - 1: "Jan", - 2: "Feb", - 3: "Mar", - 4: "Apr", - 5: "May", - 6: "Jun", - 7: "Jul", - 8: "Aug", - 9: "Sep", - 10: "Oct", - 11: "Nov", - 12: "Dec", - } - validation = { - "min_length": lambda x, y: len(x) < y, - "max_length": lambda x, y: len(x) > y, - "minimum": lambda x, y: x < y, - "maximum": lambda x, y: x > y, - "minimum_ex": lambda x, y: x <= y, - "maximum_ex": lambda x, y: x >= y, - "min_items": lambda x, y: len(x) < y, - "max_items": lambda x, y: len(x) > y, - "pattern": lambda x, y: not re.match(y, x, re.UNICODE), - "unique": lambda x, y: len(x) != len(set(x)), - "multiple": lambda x, y: x % y != 0, - } - - def __init__(self, classes: Optional[Mapping[str, type]] = None): - self.serialize_type = { - "iso-8601": Serializer.serialize_iso, - "rfc-1123": Serializer.serialize_rfc, - "unix-time": Serializer.serialize_unix, - "duration": Serializer.serialize_duration, - "date": Serializer.serialize_date, - "time": Serializer.serialize_time, - "decimal": Serializer.serialize_decimal, - "long": Serializer.serialize_long, - "bytearray": Serializer.serialize_bytearray, - "base64": Serializer.serialize_base64, - "object": self.serialize_object, - "[]": self.serialize_iter, - "{}": self.serialize_dict, - } - self.dependencies: Dict[str, type] = dict(classes) if classes else {} - self.key_transformer = full_restapi_key_transformer - self.client_side_validation = True - - def _serialize(self, target_obj, data_type=None, **kwargs): - """Serialize data into a string according to type. - - :param target_obj: The data to be serialized. - :param str data_type: The type to be serialized from. - :rtype: str, dict - :raises: SerializationError if serialization fails. - """ - key_transformer = kwargs.get("key_transformer", self.key_transformer) - keep_readonly = kwargs.get("keep_readonly", False) - if target_obj is None: - return None - - attr_name = None - class_name = target_obj.__class__.__name__ - - if data_type: - return self.serialize_data(target_obj, data_type, **kwargs) - - if not hasattr(target_obj, "_attribute_map"): - data_type = type(target_obj).__name__ - if data_type in self.basic_types.values(): - return self.serialize_data(target_obj, data_type, **kwargs) - - # Force "is_xml" kwargs if we detect a XML model - try: - is_xml_model_serialization = kwargs["is_xml"] - except KeyError: - is_xml_model_serialization = kwargs.setdefault("is_xml", target_obj.is_xml_model()) - - serialized = {} - if is_xml_model_serialization: - serialized = target_obj._create_xml_node() - try: - attributes = target_obj._attribute_map - for attr, attr_desc in attributes.items(): - attr_name = attr - if not keep_readonly and target_obj._validation.get(attr_name, {}).get("readonly", False): - continue - - if attr_name == "additional_properties" and attr_desc["key"] == "": - if target_obj.additional_properties is not None: - serialized.update(target_obj.additional_properties) - continue - try: - - orig_attr = getattr(target_obj, attr) - if is_xml_model_serialization: - pass # Don't provide "transformer" for XML for now. Keep "orig_attr" - else: # JSON - keys, orig_attr = key_transformer(attr, attr_desc.copy(), orig_attr) - keys = keys if isinstance(keys, list) else [keys] - - kwargs["serialization_ctxt"] = attr_desc - new_attr = self.serialize_data(orig_attr, attr_desc["type"], **kwargs) - - if is_xml_model_serialization: - xml_desc = attr_desc.get("xml", {}) - xml_name = xml_desc.get("name", attr_desc["key"]) - xml_prefix = xml_desc.get("prefix", None) - xml_ns = xml_desc.get("ns", None) - if xml_desc.get("attr", False): - if xml_ns: - ET.register_namespace(xml_prefix, xml_ns) - xml_name = "{{{}}}{}".format(xml_ns, xml_name) - serialized.set(xml_name, new_attr) # type: ignore - continue - if xml_desc.get("text", False): - serialized.text = new_attr # type: ignore - continue - if isinstance(new_attr, list): - serialized.extend(new_attr) # type: ignore - elif isinstance(new_attr, ET.Element): - # If the down XML has no XML/Name, we MUST replace the tag with the local tag. But keeping the namespaces. - if "name" not in getattr(orig_attr, "_xml_map", {}): - splitted_tag = new_attr.tag.split("}") - if len(splitted_tag) == 2: # Namespace - new_attr.tag = "}".join([splitted_tag[0], xml_name]) - else: - new_attr.tag = xml_name - serialized.append(new_attr) # type: ignore - else: # That's a basic type - # Integrate namespace if necessary - local_node = _create_xml_node(xml_name, xml_prefix, xml_ns) - local_node.text = str(new_attr) - serialized.append(local_node) # type: ignore - else: # JSON - for k in reversed(keys): # type: ignore - new_attr = {k: new_attr} - - _new_attr = new_attr - _serialized = serialized - for k in keys: # type: ignore - if k not in _serialized: - _serialized.update(_new_attr) # type: ignore - _new_attr = _new_attr[k] # type: ignore - _serialized = _serialized[k] - except ValueError as err: - if isinstance(err, SerializationError): - raise - - except (AttributeError, KeyError, TypeError) as err: - msg = "Attribute {} in object {} cannot be serialized.\n{}".format(attr_name, class_name, str(target_obj)) - raise SerializationError(msg) from err - else: - return serialized - - def body(self, data, data_type, **kwargs): - """Serialize data intended for a request body. - - :param data: The data to be serialized. - :param str data_type: The type to be serialized from. - :rtype: dict - :raises: SerializationError if serialization fails. - :raises: ValueError if data is None - """ - - # Just in case this is a dict - internal_data_type_str = data_type.strip("[]{}") - internal_data_type = self.dependencies.get(internal_data_type_str, None) - try: - is_xml_model_serialization = kwargs["is_xml"] - except KeyError: - if internal_data_type and issubclass(internal_data_type, Model): - is_xml_model_serialization = kwargs.setdefault("is_xml", internal_data_type.is_xml_model()) - else: - is_xml_model_serialization = False - if internal_data_type and not isinstance(internal_data_type, Enum): - try: - deserializer = Deserializer(self.dependencies) - # Since it's on serialization, it's almost sure that format is not JSON REST - # We're not able to deal with additional properties for now. - deserializer.additional_properties_detection = False - if is_xml_model_serialization: - deserializer.key_extractors = [ # type: ignore - attribute_key_case_insensitive_extractor, - ] - else: - deserializer.key_extractors = [ - rest_key_case_insensitive_extractor, - attribute_key_case_insensitive_extractor, - last_rest_key_case_insensitive_extractor, - ] - data = deserializer._deserialize(data_type, data) - except DeserializationError as err: - raise SerializationError("Unable to build a model: " + str(err)) from err - - return self._serialize(data, data_type, **kwargs) - - def url(self, name, data, data_type, **kwargs): - """Serialize data intended for a URL path. - - :param data: The data to be serialized. - :param str data_type: The type to be serialized from. - :rtype: str - :raises: TypeError if serialization fails. - :raises: ValueError if data is None - """ - try: - output = self.serialize_data(data, data_type, **kwargs) - if data_type == "bool": - output = json.dumps(output) - - if kwargs.get("skip_quote") is True: - output = str(output) - output = output.replace("{", quote("{")).replace("}", quote("}")) - else: - output = quote(str(output), safe="") - except SerializationError: - raise TypeError("{} must be type {}.".format(name, data_type)) - else: - return output - - def query(self, name, data, data_type, **kwargs): - """Serialize data intended for a URL query. - - :param data: The data to be serialized. - :param str data_type: The type to be serialized from. - :keyword bool skip_quote: Whether to skip quote the serialized result. - Defaults to False. - :rtype: str, list - :raises: TypeError if serialization fails. - :raises: ValueError if data is None - """ - try: - # Treat the list aside, since we don't want to encode the div separator - if data_type.startswith("["): - internal_data_type = data_type[1:-1] - do_quote = not kwargs.get("skip_quote", False) - return self.serialize_iter(data, internal_data_type, do_quote=do_quote, **kwargs) - - # Not a list, regular serialization - output = self.serialize_data(data, data_type, **kwargs) - if data_type == "bool": - output = json.dumps(output) - if kwargs.get("skip_quote") is True: - output = str(output) - else: - output = quote(str(output), safe="") - except SerializationError: - raise TypeError("{} must be type {}.".format(name, data_type)) - else: - return str(output) - - def header(self, name, data, data_type, **kwargs): - """Serialize data intended for a request header. - - :param data: The data to be serialized. - :param str data_type: The type to be serialized from. - :rtype: str - :raises: TypeError if serialization fails. - :raises: ValueError if data is None - """ - try: - if data_type in ["[str]"]: - data = ["" if d is None else d for d in data] - - output = self.serialize_data(data, data_type, **kwargs) - if data_type == "bool": - output = json.dumps(output) - except SerializationError: - raise TypeError("{} must be type {}.".format(name, data_type)) - else: - return str(output) - - def serialize_data(self, data, data_type, **kwargs): - """Serialize generic data according to supplied data type. - - :param data: The data to be serialized. - :param str data_type: The type to be serialized from. - :param bool required: Whether it's essential that the data not be - empty or None - :raises: AttributeError if required data is None. - :raises: ValueError if data is None - :raises: SerializationError if serialization fails. - """ - if data is None: - raise ValueError("No value for given attribute") - - try: - if data is CoreNull: - return None - if data_type in self.basic_types.values(): - return self.serialize_basic(data, data_type, **kwargs) - - elif data_type in self.serialize_type: - return self.serialize_type[data_type](data, **kwargs) - - # If dependencies is empty, try with current data class - # It has to be a subclass of Enum anyway - enum_type = self.dependencies.get(data_type, data.__class__) - if issubclass(enum_type, Enum): - return Serializer.serialize_enum(data, enum_obj=enum_type) - - iter_type = data_type[0] + data_type[-1] - if iter_type in self.serialize_type: - return self.serialize_type[iter_type](data, data_type[1:-1], **kwargs) - - except (ValueError, TypeError) as err: - msg = "Unable to serialize value: {!r} as type: {!r}." - raise SerializationError(msg.format(data, data_type)) from err - else: - return self._serialize(data, **kwargs) - - @classmethod - def _get_custom_serializers(cls, data_type, **kwargs): - custom_serializer = kwargs.get("basic_types_serializers", {}).get(data_type) - if custom_serializer: - return custom_serializer - if kwargs.get("is_xml", False): - return cls._xml_basic_types_serializers.get(data_type) - - @classmethod - def serialize_basic(cls, data, data_type, **kwargs): - """Serialize basic builting data type. - Serializes objects to str, int, float or bool. - - Possible kwargs: - - basic_types_serializers dict[str, callable] : If set, use the callable as serializer - - is_xml bool : If set, use xml_basic_types_serializers - - :param data: Object to be serialized. - :param str data_type: Type of object in the iterable. - """ - custom_serializer = cls._get_custom_serializers(data_type, **kwargs) - if custom_serializer: - return custom_serializer(data) - if data_type == "str": - return cls.serialize_unicode(data) - return eval(data_type)(data) # nosec - - @classmethod - def serialize_unicode(cls, data): - """Special handling for serializing unicode strings in Py2. - Encode to UTF-8 if unicode, otherwise handle as a str. - - :param data: Object to be serialized. - :rtype: str - """ - try: # If I received an enum, return its value - return data.value - except AttributeError: - pass - - try: - if isinstance(data, unicode): # type: ignore - # Don't change it, JSON and XML ElementTree are totally able - # to serialize correctly u'' strings - return data - except NameError: - return str(data) - else: - return str(data) - - def serialize_iter(self, data, iter_type, div=None, **kwargs): - """Serialize iterable. - - Supported kwargs: - - serialization_ctxt dict : The current entry of _attribute_map, or same format. - serialization_ctxt['type'] should be same as data_type. - - is_xml bool : If set, serialize as XML - - :param list attr: Object to be serialized. - :param str iter_type: Type of object in the iterable. - :param bool required: Whether the objects in the iterable must - not be None or empty. - :param str div: If set, this str will be used to combine the elements - in the iterable into a combined string. Default is 'None'. - :keyword bool do_quote: Whether to quote the serialized result of each iterable element. - Defaults to False. - :rtype: list, str - """ - if isinstance(data, str): - raise SerializationError("Refuse str type as a valid iter type.") - - serialization_ctxt = kwargs.get("serialization_ctxt", {}) - is_xml = kwargs.get("is_xml", False) - - serialized = [] - for d in data: - try: - serialized.append(self.serialize_data(d, iter_type, **kwargs)) - except ValueError as err: - if isinstance(err, SerializationError): - raise - serialized.append(None) - - if kwargs.get("do_quote", False): - serialized = ["" if s is None else quote(str(s), safe="") for s in serialized] - - if div: - serialized = ["" if s is None else str(s) for s in serialized] - serialized = div.join(serialized) - - if "xml" in serialization_ctxt or is_xml: - # XML serialization is more complicated - xml_desc = serialization_ctxt.get("xml", {}) - xml_name = xml_desc.get("name") - if not xml_name: - xml_name = serialization_ctxt["key"] - - # Create a wrap node if necessary (use the fact that Element and list have "append") - is_wrapped = xml_desc.get("wrapped", False) - node_name = xml_desc.get("itemsName", xml_name) - if is_wrapped: - final_result = _create_xml_node(xml_name, xml_desc.get("prefix", None), xml_desc.get("ns", None)) - else: - final_result = [] - # All list elements to "local_node" - for el in serialized: - if isinstance(el, ET.Element): - el_node = el - else: - el_node = _create_xml_node(node_name, xml_desc.get("prefix", None), xml_desc.get("ns", None)) - if el is not None: # Otherwise it writes "None" :-p - el_node.text = str(el) - final_result.append(el_node) - return final_result - return serialized - - def serialize_dict(self, attr, dict_type, **kwargs): - """Serialize a dictionary of objects. - - :param dict attr: Object to be serialized. - :param str dict_type: Type of object in the dictionary. - :param bool required: Whether the objects in the dictionary must - not be None or empty. - :rtype: dict - """ - serialization_ctxt = kwargs.get("serialization_ctxt", {}) - serialized = {} - for key, value in attr.items(): - try: - serialized[self.serialize_unicode(key)] = self.serialize_data(value, dict_type, **kwargs) - except ValueError as err: - if isinstance(err, SerializationError): - raise - serialized[self.serialize_unicode(key)] = None - - if "xml" in serialization_ctxt: - # XML serialization is more complicated - xml_desc = serialization_ctxt["xml"] - xml_name = xml_desc["name"] - - final_result = _create_xml_node(xml_name, xml_desc.get("prefix", None), xml_desc.get("ns", None)) - for key, value in serialized.items(): - ET.SubElement(final_result, key).text = value - return final_result - - return serialized - - def serialize_object(self, attr, **kwargs): - """Serialize a generic object. - This will be handled as a dictionary. If object passed in is not - a basic type (str, int, float, dict, list) it will simply be - cast to str. - - :param dict attr: Object to be serialized. - :rtype: dict or str - """ - if attr is None: - return None - if isinstance(attr, ET.Element): - return attr - obj_type = type(attr) - if obj_type in self.basic_types: - return self.serialize_basic(attr, self.basic_types[obj_type], **kwargs) - if obj_type is _long_type: - return self.serialize_long(attr) - if obj_type is str: - return self.serialize_unicode(attr) - if obj_type is datetime.datetime: - return self.serialize_iso(attr) - if obj_type is datetime.date: - return self.serialize_date(attr) - if obj_type is datetime.time: - return self.serialize_time(attr) - if obj_type is datetime.timedelta: - return self.serialize_duration(attr) - if obj_type is decimal.Decimal: - return self.serialize_decimal(attr) - - # If it's a model or I know this dependency, serialize as a Model - elif obj_type in self.dependencies.values() or isinstance(attr, Model): - return self._serialize(attr) - - if obj_type == dict: - serialized = {} - for key, value in attr.items(): - try: - serialized[self.serialize_unicode(key)] = self.serialize_object(value, **kwargs) - except ValueError: - serialized[self.serialize_unicode(key)] = None - return serialized - - if obj_type == list: - serialized = [] - for obj in attr: - try: - serialized.append(self.serialize_object(obj, **kwargs)) - except ValueError: - pass - return serialized - return str(attr) - - @staticmethod - def serialize_enum(attr, enum_obj=None): - try: - result = attr.value - except AttributeError: - result = attr - try: - enum_obj(result) # type: ignore - return result - except ValueError: - for enum_value in enum_obj: # type: ignore - if enum_value.value.lower() == str(attr).lower(): - return enum_value.value - error = "{!r} is not valid value for enum {!r}" - raise SerializationError(error.format(attr, enum_obj)) - - @staticmethod - def serialize_bytearray(attr, **kwargs): - """Serialize bytearray into base-64 string. - - :param attr: Object to be serialized. - :rtype: str - """ - return b64encode(attr).decode() - - @staticmethod - def serialize_base64(attr, **kwargs): - """Serialize str into base-64 string. - - :param attr: Object to be serialized. - :rtype: str - """ - encoded = b64encode(attr).decode("ascii") - return encoded.strip("=").replace("+", "-").replace("/", "_") - - @staticmethod - def serialize_decimal(attr, **kwargs): - """Serialize Decimal object to float. - - :param attr: Object to be serialized. - :rtype: float - """ - return float(attr) - - @staticmethod - def serialize_long(attr, **kwargs): - """Serialize long (Py2) or int (Py3). - - :param attr: Object to be serialized. - :rtype: int/long - """ - return _long_type(attr) - - @staticmethod - def serialize_date(attr, **kwargs): - """Serialize Date object into ISO-8601 formatted string. - - :param Date attr: Object to be serialized. - :rtype: str - """ - if isinstance(attr, str): - attr = isodate.parse_date(attr) - t = "{:04}-{:02}-{:02}".format(attr.year, attr.month, attr.day) - return t - - @staticmethod - def serialize_time(attr, **kwargs): - """Serialize Time object into ISO-8601 formatted string. - - :param datetime.time attr: Object to be serialized. - :rtype: str - """ - if isinstance(attr, str): - attr = isodate.parse_time(attr) - t = "{:02}:{:02}:{:02}".format(attr.hour, attr.minute, attr.second) - if attr.microsecond: - t += ".{:02}".format(attr.microsecond) - return t - - @staticmethod - def serialize_duration(attr, **kwargs): - """Serialize TimeDelta object into ISO-8601 formatted string. - - :param TimeDelta attr: Object to be serialized. - :rtype: str - """ - if isinstance(attr, str): - attr = isodate.parse_duration(attr) - return isodate.duration_isoformat(attr) - - @staticmethod - def serialize_rfc(attr, **kwargs): - """Serialize Datetime object into RFC-1123 formatted string. - - :param Datetime attr: Object to be serialized. - :rtype: str - :raises: TypeError if format invalid. - """ - try: - if not attr.tzinfo: - _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") - utc = attr.utctimetuple() - except AttributeError: - raise TypeError("RFC1123 object must be valid Datetime object.") - - return "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT".format( - Serializer.days[utc.tm_wday], - utc.tm_mday, - Serializer.months[utc.tm_mon], - utc.tm_year, - utc.tm_hour, - utc.tm_min, - utc.tm_sec, - ) - - @staticmethod - def serialize_iso(attr, **kwargs): - """Serialize Datetime object into ISO-8601 formatted string. - - :param Datetime attr: Object to be serialized. - :rtype: str - :raises: SerializationError if format invalid. - """ - if isinstance(attr, str): - attr = isodate.parse_datetime(attr) - try: - if not attr.tzinfo: - _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") - utc = attr.utctimetuple() - if utc.tm_year > 9999 or utc.tm_year < 1: - raise OverflowError("Hit max or min date") - - microseconds = str(attr.microsecond).rjust(6, "0").rstrip("0").ljust(3, "0") - if microseconds: - microseconds = "." + microseconds - date = "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}".format( - utc.tm_year, utc.tm_mon, utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec - ) - return date + microseconds + "Z" - except (ValueError, OverflowError) as err: - msg = "Unable to serialize datetime object." - raise SerializationError(msg) from err - except AttributeError as err: - msg = "ISO-8601 object must be valid Datetime object." - raise TypeError(msg) from err - - @staticmethod - def serialize_unix(attr, **kwargs): - """Serialize Datetime object into IntTime format. - This is represented as seconds. - - :param Datetime attr: Object to be serialized. - :rtype: int - :raises: SerializationError if format invalid - """ - if isinstance(attr, int): - return attr - try: - if not attr.tzinfo: - _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") - return int(calendar.timegm(attr.utctimetuple())) - except AttributeError: - raise TypeError("Unix time object must be valid Datetime object.") - - -def rest_key_extractor(attr, attr_desc, data): - key = attr_desc["key"] - working_data = data - - while "." in key: - # Need the cast, as for some reasons "split" is typed as list[str | Any] - dict_keys = cast(List[str], _FLATTEN.split(key)) - if len(dict_keys) == 1: - key = _decode_attribute_map_key(dict_keys[0]) - break - working_key = _decode_attribute_map_key(dict_keys[0]) - working_data = working_data.get(working_key, data) - if working_data is None: - # If at any point while following flatten JSON path see None, it means - # that all properties under are None as well - return None - key = ".".join(dict_keys[1:]) - - return working_data.get(key) - - -def rest_key_case_insensitive_extractor(attr, attr_desc, data): - key = attr_desc["key"] - working_data = data - - while "." in key: - dict_keys = _FLATTEN.split(key) - if len(dict_keys) == 1: - key = _decode_attribute_map_key(dict_keys[0]) - break - working_key = _decode_attribute_map_key(dict_keys[0]) - working_data = attribute_key_case_insensitive_extractor(working_key, None, working_data) - if working_data is None: - # If at any point while following flatten JSON path see None, it means - # that all properties under are None as well - return None - key = ".".join(dict_keys[1:]) - - if working_data: - return attribute_key_case_insensitive_extractor(key, None, working_data) - - -def last_rest_key_extractor(attr, attr_desc, data): - """Extract the attribute in "data" based on the last part of the JSON path key.""" - key = attr_desc["key"] - dict_keys = _FLATTEN.split(key) - return attribute_key_extractor(dict_keys[-1], None, data) - - -def last_rest_key_case_insensitive_extractor(attr, attr_desc, data): - """Extract the attribute in "data" based on the last part of the JSON path key. - - This is the case insensitive version of "last_rest_key_extractor" - """ - key = attr_desc["key"] - dict_keys = _FLATTEN.split(key) - return attribute_key_case_insensitive_extractor(dict_keys[-1], None, data) - - -def attribute_key_extractor(attr, _, data): - return data.get(attr) - - -def attribute_key_case_insensitive_extractor(attr, _, data): - found_key = None - lower_attr = attr.lower() - for key in data: - if lower_attr == key.lower(): - found_key = key - break - - return data.get(found_key) - - -def _extract_name_from_internal_type(internal_type): - """Given an internal type XML description, extract correct XML name with namespace. - - :param dict internal_type: An model type - :rtype: tuple - :returns: A tuple XML name + namespace dict - """ - internal_type_xml_map = getattr(internal_type, "_xml_map", {}) - xml_name = internal_type_xml_map.get("name", internal_type.__name__) - xml_ns = internal_type_xml_map.get("ns", None) - if xml_ns: - xml_name = "{{{}}}{}".format(xml_ns, xml_name) - return xml_name - - -def xml_key_extractor(attr, attr_desc, data): - if isinstance(data, dict): - return None - - # Test if this model is XML ready first - if not isinstance(data, ET.Element): - return None - - xml_desc = attr_desc.get("xml", {}) - xml_name = xml_desc.get("name", attr_desc["key"]) - - # Look for a children - is_iter_type = attr_desc["type"].startswith("[") - is_wrapped = xml_desc.get("wrapped", False) - internal_type = attr_desc.get("internalType", None) - internal_type_xml_map = getattr(internal_type, "_xml_map", {}) - - # Integrate namespace if necessary - xml_ns = xml_desc.get("ns", internal_type_xml_map.get("ns", None)) - if xml_ns: - xml_name = "{{{}}}{}".format(xml_ns, xml_name) - - # If it's an attribute, that's simple - if xml_desc.get("attr", False): - return data.get(xml_name) - - # If it's x-ms-text, that's simple too - if xml_desc.get("text", False): - return data.text - - # Scenario where I take the local name: - # - Wrapped node - # - Internal type is an enum (considered basic types) - # - Internal type has no XML/Name node - if is_wrapped or (internal_type and (issubclass(internal_type, Enum) or "name" not in internal_type_xml_map)): - children = data.findall(xml_name) - # If internal type has a local name and it's not a list, I use that name - elif not is_iter_type and internal_type and "name" in internal_type_xml_map: - xml_name = _extract_name_from_internal_type(internal_type) - children = data.findall(xml_name) - # That's an array - else: - if internal_type: # Complex type, ignore itemsName and use the complex type name - items_name = _extract_name_from_internal_type(internal_type) - else: - items_name = xml_desc.get("itemsName", xml_name) - children = data.findall(items_name) - - if len(children) == 0: - if is_iter_type: - if is_wrapped: - return None # is_wrapped no node, we want None - else: - return [] # not wrapped, assume empty list - return None # Assume it's not there, maybe an optional node. - - # If is_iter_type and not wrapped, return all found children - if is_iter_type: - if not is_wrapped: - return children - else: # Iter and wrapped, should have found one node only (the wrap one) - if len(children) != 1: - raise DeserializationError( - "Tried to deserialize an array not wrapped, and found several nodes '{}'. Maybe you should declare this array as wrapped?".format( - xml_name - ) - ) - return list(children[0]) # Might be empty list and that's ok. - - # Here it's not a itertype, we should have found one element only or empty - if len(children) > 1: - raise DeserializationError("Find several XML '{}' where it was not expected".format(xml_name)) - return children[0] - - -class Deserializer(object): - """Response object model deserializer. - - :param dict classes: Class type dictionary for deserializing complex types. - :ivar list key_extractors: Ordered list of extractors to be used by this deserializer. - """ - - basic_types = {str: "str", int: "int", bool: "bool", float: "float"} - - valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}" r"\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?") - - def __init__(self, classes: Optional[Mapping[str, type]] = None): - self.deserialize_type = { - "iso-8601": Deserializer.deserialize_iso, - "rfc-1123": Deserializer.deserialize_rfc, - "unix-time": Deserializer.deserialize_unix, - "duration": Deserializer.deserialize_duration, - "date": Deserializer.deserialize_date, - "time": Deserializer.deserialize_time, - "decimal": Deserializer.deserialize_decimal, - "long": Deserializer.deserialize_long, - "bytearray": Deserializer.deserialize_bytearray, - "base64": Deserializer.deserialize_base64, - "object": self.deserialize_object, - "[]": self.deserialize_iter, - "{}": self.deserialize_dict, - } - self.deserialize_expected_types = { - "duration": (isodate.Duration, datetime.timedelta), - "iso-8601": (datetime.datetime), - } - self.dependencies: Dict[str, type] = dict(classes) if classes else {} - self.key_extractors = [rest_key_extractor, xml_key_extractor] - # Additional properties only works if the "rest_key_extractor" is used to - # extract the keys. Making it to work whatever the key extractor is too much - # complicated, with no real scenario for now. - # So adding a flag to disable additional properties detection. This flag should be - # used if your expect the deserialization to NOT come from a JSON REST syntax. - # Otherwise, result are unexpected - self.additional_properties_detection = True - - def __call__(self, target_obj, response_data, content_type=None): - """Call the deserializer to process a REST response. - - :param str target_obj: Target data type to deserialize to. - :param requests.Response response_data: REST response object. - :param str content_type: Swagger "produces" if available. - :raises: DeserializationError if deserialization fails. - :return: Deserialized object. - """ - data = self._unpack_content(response_data, content_type) - return self._deserialize(target_obj, data) - - def _deserialize(self, target_obj, data): - """Call the deserializer on a model. - - Data needs to be already deserialized as JSON or XML ElementTree - - :param str target_obj: Target data type to deserialize to. - :param object data: Object to deserialize. - :raises: DeserializationError if deserialization fails. - :return: Deserialized object. - """ - # This is already a model, go recursive just in case - if hasattr(data, "_attribute_map"): - constants = [name for name, config in getattr(data, "_validation", {}).items() if config.get("constant")] - try: - for attr, mapconfig in data._attribute_map.items(): - if attr in constants: - continue - value = getattr(data, attr) - if value is None: - continue - local_type = mapconfig["type"] - internal_data_type = local_type.strip("[]{}") - if internal_data_type not in self.dependencies or isinstance(internal_data_type, Enum): - continue - setattr(data, attr, self._deserialize(local_type, value)) - return data - except AttributeError: - return - - response, class_name = self._classify_target(target_obj, data) - - if isinstance(response, str): - return self.deserialize_data(data, response) - elif isinstance(response, type) and issubclass(response, Enum): - return self.deserialize_enum(data, response) - - if data is None: - return data - try: - attributes = response._attribute_map # type: ignore - d_attrs = {} - for attr, attr_desc in attributes.items(): - # Check empty string. If it's not empty, someone has a real "additionalProperties"... - if attr == "additional_properties" and attr_desc["key"] == "": - continue - raw_value = None - # Enhance attr_desc with some dynamic data - attr_desc = attr_desc.copy() # Do a copy, do not change the real one - internal_data_type = attr_desc["type"].strip("[]{}") - if internal_data_type in self.dependencies: - attr_desc["internalType"] = self.dependencies[internal_data_type] - - for key_extractor in self.key_extractors: - found_value = key_extractor(attr, attr_desc, data) - if found_value is not None: - if raw_value is not None and raw_value != found_value: - msg = ( - "Ignoring extracted value '%s' from %s for key '%s'" - " (duplicate extraction, follow extractors order)" - ) - _LOGGER.warning(msg, found_value, key_extractor, attr) - continue - raw_value = found_value - - value = self.deserialize_data(raw_value, attr_desc["type"]) - d_attrs[attr] = value - except (AttributeError, TypeError, KeyError) as err: - msg = "Unable to deserialize to object: " + class_name # type: ignore - raise DeserializationError(msg) from err - else: - additional_properties = self._build_additional_properties(attributes, data) - return self._instantiate_model(response, d_attrs, additional_properties) - - def _build_additional_properties(self, attribute_map, data): - if not self.additional_properties_detection: - return None - if "additional_properties" in attribute_map and attribute_map.get("additional_properties", {}).get("key") != "": - # Check empty string. If it's not empty, someone has a real "additionalProperties" - return None - if isinstance(data, ET.Element): - data = {el.tag: el.text for el in data} - - known_keys = { - _decode_attribute_map_key(_FLATTEN.split(desc["key"])[0]) - for desc in attribute_map.values() - if desc["key"] != "" - } - present_keys = set(data.keys()) - missing_keys = present_keys - known_keys - return {key: data[key] for key in missing_keys} - - def _classify_target(self, target, data): - """Check to see whether the deserialization target object can - be classified into a subclass. - Once classification has been determined, initialize object. - - :param str target: The target object type to deserialize to. - :param str/dict data: The response data to deserialize. - """ - if target is None: - return None, None - - if isinstance(target, str): - try: - target = self.dependencies[target] - except KeyError: - return target, target - - try: - target = target._classify(data, self.dependencies) # type: ignore - except AttributeError: - pass # Target is not a Model, no classify - return target, target.__class__.__name__ # type: ignore - - def failsafe_deserialize(self, target_obj, data, content_type=None): - """Ignores any errors encountered in deserialization, - and falls back to not deserializing the object. Recommended - for use in error deserialization, as we want to return the - HttpResponseError to users, and not have them deal with - a deserialization error. - - :param str target_obj: The target object type to deserialize to. - :param str/dict data: The response data to deserialize. - :param str content_type: Swagger "produces" if available. - """ - try: - return self(target_obj, data, content_type=content_type) - except: - _LOGGER.debug( - "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True - ) - return None - - @staticmethod - def _unpack_content(raw_data, content_type=None): - """Extract the correct structure for deserialization. - - If raw_data is a PipelineResponse, try to extract the result of RawDeserializer. - if we can't, raise. Your Pipeline should have a RawDeserializer. - - If not a pipeline response and raw_data is bytes or string, use content-type - to decode it. If no content-type, try JSON. - - If raw_data is something else, bypass all logic and return it directly. - - :param raw_data: Data to be processed. - :param content_type: How to parse if raw_data is a string/bytes. - :raises JSONDecodeError: If JSON is requested and parsing is impossible. - :raises UnicodeDecodeError: If bytes is not UTF8 - """ - # Assume this is enough to detect a Pipeline Response without importing it - context = getattr(raw_data, "context", {}) - if context: - if RawDeserializer.CONTEXT_NAME in context: - return context[RawDeserializer.CONTEXT_NAME] - raise ValueError("This pipeline didn't have the RawDeserializer policy; can't deserialize") - - # Assume this is enough to recognize universal_http.ClientResponse without importing it - if hasattr(raw_data, "body"): - return RawDeserializer.deserialize_from_http_generics(raw_data.text(), raw_data.headers) - - # Assume this enough to recognize requests.Response without importing it. - if hasattr(raw_data, "_content_consumed"): - return RawDeserializer.deserialize_from_http_generics(raw_data.text, raw_data.headers) - - if isinstance(raw_data, (str, bytes)) or hasattr(raw_data, "read"): - return RawDeserializer.deserialize_from_text(raw_data, content_type) # type: ignore - return raw_data - - def _instantiate_model(self, response, attrs, additional_properties=None): - """Instantiate a response model passing in deserialized args. - - :param response: The response model class. - :param d_attrs: The deserialized response attributes. - """ - if callable(response): - subtype = getattr(response, "_subtype_map", {}) - try: - readonly = [k for k, v in response._validation.items() if v.get("readonly")] - const = [k for k, v in response._validation.items() if v.get("constant")] - kwargs = {k: v for k, v in attrs.items() if k not in subtype and k not in readonly + const} - response_obj = response(**kwargs) - for attr in readonly: - setattr(response_obj, attr, attrs.get(attr)) - if additional_properties: - response_obj.additional_properties = additional_properties - return response_obj - except TypeError as err: - msg = "Unable to deserialize {} into model {}. ".format(kwargs, response) # type: ignore - raise DeserializationError(msg + str(err)) - else: - try: - for attr, value in attrs.items(): - setattr(response, attr, value) - return response - except Exception as exp: - msg = "Unable to populate response model. " - msg += "Type: {}, Error: {}".format(type(response), exp) - raise DeserializationError(msg) - - def deserialize_data(self, data, data_type): - """Process data for deserialization according to data type. - - :param str data: The response string to be deserialized. - :param str data_type: The type to deserialize to. - :raises: DeserializationError if deserialization fails. - :return: Deserialized object. - """ - if data is None: - return data - - try: - if not data_type: - return data - if data_type in self.basic_types.values(): - return self.deserialize_basic(data, data_type) - if data_type in self.deserialize_type: - if isinstance(data, self.deserialize_expected_types.get(data_type, tuple())): - return data - - is_a_text_parsing_type = lambda x: x not in ["object", "[]", r"{}"] - if isinstance(data, ET.Element) and is_a_text_parsing_type(data_type) and not data.text: - return None - data_val = self.deserialize_type[data_type](data) - return data_val - - iter_type = data_type[0] + data_type[-1] - if iter_type in self.deserialize_type: - return self.deserialize_type[iter_type](data, data_type[1:-1]) - - obj_type = self.dependencies[data_type] - if issubclass(obj_type, Enum): - if isinstance(data, ET.Element): - data = data.text - return self.deserialize_enum(data, obj_type) - - except (ValueError, TypeError, AttributeError) as err: - msg = "Unable to deserialize response data." - msg += " Data: {}, {}".format(data, data_type) - raise DeserializationError(msg) from err - else: - return self._deserialize(obj_type, data) - - def deserialize_iter(self, attr, iter_type): - """Deserialize an iterable. - - :param list attr: Iterable to be deserialized. - :param str iter_type: The type of object in the iterable. - :rtype: list - """ - if attr is None: - return None - if isinstance(attr, ET.Element): # If I receive an element here, get the children - attr = list(attr) - if not isinstance(attr, (list, set)): - raise DeserializationError("Cannot deserialize as [{}] an object of type {}".format(iter_type, type(attr))) - return [self.deserialize_data(a, iter_type) for a in attr] - - def deserialize_dict(self, attr, dict_type): - """Deserialize a dictionary. - - :param dict/list attr: Dictionary to be deserialized. Also accepts - a list of key, value pairs. - :param str dict_type: The object type of the items in the dictionary. - :rtype: dict - """ - if isinstance(attr, list): - return {x["key"]: self.deserialize_data(x["value"], dict_type) for x in attr} - - if isinstance(attr, ET.Element): - # Transform value into {"Key": "value"} - attr = {el.tag: el.text for el in attr} - return {k: self.deserialize_data(v, dict_type) for k, v in attr.items()} - - def deserialize_object(self, attr, **kwargs): - """Deserialize a generic object. - This will be handled as a dictionary. - - :param dict attr: Dictionary to be deserialized. - :rtype: dict - :raises: TypeError if non-builtin datatype encountered. - """ - if attr is None: - return None - if isinstance(attr, ET.Element): - # Do no recurse on XML, just return the tree as-is - return attr - if isinstance(attr, str): - return self.deserialize_basic(attr, "str") - obj_type = type(attr) - if obj_type in self.basic_types: - return self.deserialize_basic(attr, self.basic_types[obj_type]) - if obj_type is _long_type: - return self.deserialize_long(attr) - - if obj_type == dict: - deserialized = {} - for key, value in attr.items(): - try: - deserialized[key] = self.deserialize_object(value, **kwargs) - except ValueError: - deserialized[key] = None - return deserialized - - if obj_type == list: - deserialized = [] - for obj in attr: - try: - deserialized.append(self.deserialize_object(obj, **kwargs)) - except ValueError: - pass - return deserialized - - else: - error = "Cannot deserialize generic object with type: " - raise TypeError(error + str(obj_type)) - - def deserialize_basic(self, attr, data_type): - """Deserialize basic builtin data type from string. - Will attempt to convert to str, int, float and bool. - This function will also accept '1', '0', 'true' and 'false' as - valid bool values. - - :param str attr: response string to be deserialized. - :param str data_type: deserialization data type. - :rtype: str, int, float or bool - :raises: TypeError if string format is not valid. - """ - # If we're here, data is supposed to be a basic type. - # If it's still an XML node, take the text - if isinstance(attr, ET.Element): - attr = attr.text - if not attr: - if data_type == "str": - # None or '', node is empty string. - return "" - else: - # None or '', node with a strong type is None. - # Don't try to model "empty bool" or "empty int" - return None - - if data_type == "bool": - if attr in [True, False, 1, 0]: - return bool(attr) - elif isinstance(attr, str): - if attr.lower() in ["true", "1"]: - return True - elif attr.lower() in ["false", "0"]: - return False - raise TypeError("Invalid boolean value: {}".format(attr)) - - if data_type == "str": - return self.deserialize_unicode(attr) - return eval(data_type)(attr) # nosec - - @staticmethod - def deserialize_unicode(data): - """Preserve unicode objects in Python 2, otherwise return data - as a string. - - :param str data: response string to be deserialized. - :rtype: str or unicode - """ - # We might be here because we have an enum modeled as string, - # and we try to deserialize a partial dict with enum inside - if isinstance(data, Enum): - return data - - # Consider this is real string - try: - if isinstance(data, unicode): # type: ignore - return data - except NameError: - return str(data) - else: - return str(data) - - @staticmethod - def deserialize_enum(data, enum_obj): - """Deserialize string into enum object. - - If the string is not a valid enum value it will be returned as-is - and a warning will be logged. - - :param str data: Response string to be deserialized. If this value is - None or invalid it will be returned as-is. - :param Enum enum_obj: Enum object to deserialize to. - :rtype: Enum - """ - if isinstance(data, enum_obj) or data is None: - return data - if isinstance(data, Enum): - data = data.value - if isinstance(data, int): - # Workaround. We might consider remove it in the future. - try: - return list(enum_obj.__members__.values())[data] - except IndexError: - error = "{!r} is not a valid index for enum {!r}" - raise DeserializationError(error.format(data, enum_obj)) - try: - return enum_obj(str(data)) - except ValueError: - for enum_value in enum_obj: - if enum_value.value.lower() == str(data).lower(): - return enum_value - # We don't fail anymore for unknown value, we deserialize as a string - _LOGGER.warning("Deserializer is not able to find %s as valid enum in %s", data, enum_obj) - return Deserializer.deserialize_unicode(data) - - @staticmethod - def deserialize_bytearray(attr): - """Deserialize string into bytearray. - - :param str attr: response string to be deserialized. - :rtype: bytearray - :raises: TypeError if string format invalid. - """ - if isinstance(attr, ET.Element): - attr = attr.text - return bytearray(b64decode(attr)) # type: ignore - - @staticmethod - def deserialize_base64(attr): - """Deserialize base64 encoded string into string. - - :param str attr: response string to be deserialized. - :rtype: bytearray - :raises: TypeError if string format invalid. - """ - if isinstance(attr, ET.Element): - attr = attr.text - padding = "=" * (3 - (len(attr) + 3) % 4) # type: ignore - attr = attr + padding # type: ignore - encoded = attr.replace("-", "+").replace("_", "/") - return b64decode(encoded) - - @staticmethod - def deserialize_decimal(attr): - """Deserialize string into Decimal object. - - :param str attr: response string to be deserialized. - :rtype: Decimal - :raises: DeserializationError if string format invalid. - """ - if isinstance(attr, ET.Element): - attr = attr.text - try: - return decimal.Decimal(str(attr)) # type: ignore - except decimal.DecimalException as err: - msg = "Invalid decimal {}".format(attr) - raise DeserializationError(msg) from err - - @staticmethod - def deserialize_long(attr): - """Deserialize string into long (Py2) or int (Py3). - - :param str attr: response string to be deserialized. - :rtype: long or int - :raises: ValueError if string format invalid. - """ - if isinstance(attr, ET.Element): - attr = attr.text - return _long_type(attr) # type: ignore - - @staticmethod - def deserialize_duration(attr): - """Deserialize ISO-8601 formatted string into TimeDelta object. - - :param str attr: response string to be deserialized. - :rtype: TimeDelta - :raises: DeserializationError if string format invalid. - """ - if isinstance(attr, ET.Element): - attr = attr.text - try: - duration = isodate.parse_duration(attr) - except (ValueError, OverflowError, AttributeError) as err: - msg = "Cannot deserialize duration object." - raise DeserializationError(msg) from err - else: - return duration - - @staticmethod - def deserialize_date(attr): - """Deserialize ISO-8601 formatted string into Date object. - - :param str attr: response string to be deserialized. - :rtype: Date - :raises: DeserializationError if string format invalid. - """ - if isinstance(attr, ET.Element): - attr = attr.text - if re.search(r"[^\W\d_]", attr, re.I + re.U): # type: ignore - raise DeserializationError("Date must have only digits and -. Received: %s" % attr) - # This must NOT use defaultmonth/defaultday. Using None ensure this raises an exception. - return isodate.parse_date(attr, defaultmonth=0, defaultday=0) - - @staticmethod - def deserialize_time(attr): - """Deserialize ISO-8601 formatted string into time object. - - :param str attr: response string to be deserialized. - :rtype: datetime.time - :raises: DeserializationError if string format invalid. - """ - if isinstance(attr, ET.Element): - attr = attr.text - if re.search(r"[^\W\d_]", attr, re.I + re.U): # type: ignore - raise DeserializationError("Date must have only digits and -. Received: %s" % attr) - return isodate.parse_time(attr) - - @staticmethod - def deserialize_rfc(attr): - """Deserialize RFC-1123 formatted string into Datetime object. - - :param str attr: response string to be deserialized. - :rtype: Datetime - :raises: DeserializationError if string format invalid. - """ - if isinstance(attr, ET.Element): - attr = attr.text - try: - parsed_date = email.utils.parsedate_tz(attr) # type: ignore - date_obj = datetime.datetime( - *parsed_date[:6], tzinfo=_FixedOffset(datetime.timedelta(minutes=(parsed_date[9] or 0) / 60)) - ) - if not date_obj.tzinfo: - date_obj = date_obj.astimezone(tz=TZ_UTC) - except ValueError as err: - msg = "Cannot deserialize to rfc datetime object." - raise DeserializationError(msg) from err - else: - return date_obj - - @staticmethod - def deserialize_iso(attr): - """Deserialize ISO-8601 formatted string into Datetime object. - - :param str attr: response string to be deserialized. - :rtype: Datetime - :raises: DeserializationError if string format invalid. - """ - if isinstance(attr, ET.Element): - attr = attr.text - try: - attr = attr.upper() # type: ignore - match = Deserializer.valid_date.match(attr) - if not match: - raise ValueError("Invalid datetime string: " + attr) - - check_decimal = attr.split(".") - if len(check_decimal) > 1: - decimal_str = "" - for digit in check_decimal[1]: - if digit.isdigit(): - decimal_str += digit - else: - break - if len(decimal_str) > 6: - attr = attr.replace(decimal_str, decimal_str[0:6]) - - date_obj = isodate.parse_datetime(attr) - test_utc = date_obj.utctimetuple() - if test_utc.tm_year > 9999 or test_utc.tm_year < 1: - raise OverflowError("Hit max or min date") - except (ValueError, OverflowError, AttributeError) as err: - msg = "Cannot deserialize datetime object." - raise DeserializationError(msg) from err - else: - return date_obj - - @staticmethod - def deserialize_unix(attr): - """Serialize Datetime object into IntTime format. - This is represented as seconds. - - :param int attr: Object to be serialized. - :rtype: Datetime - :raises: DeserializationError if format invalid - """ - if isinstance(attr, ET.Element): - attr = int(attr.text) # type: ignore - try: - attr = int(attr) - date_obj = datetime.datetime.fromtimestamp(attr, TZ_UTC) - except ValueError as err: - msg = "Cannot deserialize to unix datetime object." - raise DeserializationError(msg) from err - else: - return date_obj diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/__init__.py deleted file mode 100644 index 645065b21cba..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from ._client import AuthoringClient - -try: - from ._patch import __all__ as _patch_all - from ._patch import * # pylint: disable=unused-wildcard-import -except ImportError: - _patch_all = [] -from ._patch import patch_sdk as _patch_sdk - -__all__ = [ - "AuthoringClient", -] -__all__.extend([p for p in _patch_all if p not in __all__]) - -_patch_sdk() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_client.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_client.py deleted file mode 100644 index 0dd300032b52..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_client.py +++ /dev/null @@ -1,103 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from copy import deepcopy -from typing import Any, Awaitable - -from azure.core import AsyncPipelineClient -from azure.core.credentials import AzureKeyCredential -from azure.core.pipeline import policies -from azure.core.rest import AsyncHttpResponse, HttpRequest - -from .._serialization import Deserializer, Serializer -from ._configuration import AuthoringClientConfiguration -from ._operations import AuthoringClientOperationsMixin - - -class AuthoringClient(AuthoringClientOperationsMixin): # pylint: disable=client-accepts-api-version-keyword - """The language service API is a suite of natural language processing (NLP) skills built with - best-in-class Microsoft machine learning algorithms. The API can be used to analyze - unstructured text for tasks such as sentiment analysis, key phrase extraction, language - detection and question answering. Further documentation can be found in - https://learn.microsoft.com/azure/cognitive-services/text-analytics/overview. - - :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.api.cognitiveservices.azure.com). Required. - :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. Required. - :type credential: ~azure.core.credentials.AzureKeyCredential - :keyword api_version: Api Version. Default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. - :paramtype api_version: str - :keyword int polling_interval: Default waiting time between two polls for LRO operations if no - Retry-After header is present. - """ - - def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) -> None: - _endpoint = "{Endpoint}/language" - self._config = AuthoringClientConfiguration(endpoint=endpoint, credential=credential, **kwargs) - _policies = kwargs.pop("policies", None) - if _policies is None: - _policies = [ - policies.RequestIdPolicy(**kwargs), - self._config.headers_policy, - self._config.user_agent_policy, - self._config.proxy_policy, - policies.ContentDecodePolicy(**kwargs), - self._config.redirect_policy, - self._config.retry_policy, - self._config.authentication_policy, - self._config.custom_hook_policy, - self._config.logging_policy, - policies.DistributedTracingPolicy(**kwargs), - policies.SensitiveHeaderCleanupPolicy(**kwargs) if self._config.redirect_policy else None, - self._config.http_logging_policy, - ] - self._client: AsyncPipelineClient = AsyncPipelineClient(base_url=_endpoint, policies=_policies, **kwargs) - - self._serialize = Serializer() - self._deserialize = Deserializer() - self._serialize.client_side_validation = False - - def send_request( - self, request: HttpRequest, *, stream: bool = False, **kwargs: Any - ) -> Awaitable[AsyncHttpResponse]: - """Runs the network request through the client's chained policies. - - >>> from azure.core.rest import HttpRequest - >>> request = HttpRequest("GET", "https://www.example.org/") - - >>> response = await client.send_request(request) - - - For more information on this code flow, see https://aka.ms/azsdk/dpcodegen/python/send_request - - :param request: The network request you want to make. Required. - :type request: ~azure.core.rest.HttpRequest - :keyword bool stream: Whether the response payload will be streamed. Defaults to False. - :return: The response of your network call. Does not do error handling on your response. - :rtype: ~azure.core.rest.AsyncHttpResponse - """ - - request_copy = deepcopy(request) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments) - return self._client.send_request(request_copy, stream=stream, **kwargs) # type: ignore - - async def close(self) -> None: - await self._client.close() - - async def __aenter__(self) -> "AuthoringClient": - await self._client.__aenter__() - return self - - async def __aexit__(self, *exc_details: Any) -> None: - await self._client.__aexit__(*exc_details) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_configuration.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_configuration.py deleted file mode 100644 index 31299bf7fe3a..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_configuration.py +++ /dev/null @@ -1,61 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from typing import Any - -from azure.core.credentials import AzureKeyCredential -from azure.core.pipeline import policies - -from .._version import VERSION - - -class AuthoringClientConfiguration: # pylint: disable=too-many-instance-attributes,name-too-long - """Configuration for AuthoringClient. - - Note that all parameters used to create this instance are saved as instance - attributes. - - :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.api.cognitiveservices.azure.com). Required. - :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. Required. - :type credential: ~azure.core.credentials.AzureKeyCredential - :keyword api_version: Api Version. Default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. - :paramtype api_version: str - """ - - def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) -> None: - api_version: str = kwargs.pop("api_version", "2021-10-01") - - if endpoint is None: - raise ValueError("Parameter 'endpoint' must not be None.") - if credential is None: - raise ValueError("Parameter 'credential' must not be None.") - - self.endpoint = endpoint - self.credential = credential - self.api_version = api_version - kwargs.setdefault("sdk_moniker", "ai-language-questionanswering/{}".format(VERSION)) - self.polling_interval = kwargs.get("polling_interval", 30) - self._configure(**kwargs) - - def _configure(self, **kwargs: Any) -> None: - self.user_agent_policy = kwargs.get("user_agent_policy") or policies.UserAgentPolicy(**kwargs) - self.headers_policy = kwargs.get("headers_policy") or policies.HeadersPolicy(**kwargs) - self.proxy_policy = kwargs.get("proxy_policy") or policies.ProxyPolicy(**kwargs) - self.logging_policy = kwargs.get("logging_policy") or policies.NetworkTraceLoggingPolicy(**kwargs) - self.http_logging_policy = kwargs.get("http_logging_policy") or policies.HttpLoggingPolicy(**kwargs) - self.custom_hook_policy = kwargs.get("custom_hook_policy") or policies.CustomHookPolicy(**kwargs) - self.redirect_policy = kwargs.get("redirect_policy") or policies.AsyncRedirectPolicy(**kwargs) - self.retry_policy = kwargs.get("retry_policy") or policies.AsyncRetryPolicy(**kwargs) - self.authentication_policy = kwargs.get("authentication_policy") - if self.credential and not self.authentication_policy: - self.authentication_policy = policies.AzureKeyCredentialPolicy( - self.credential, "Ocp-Apim-Subscription-Key", **kwargs - ) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_operations/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_operations/__init__.py deleted file mode 100644 index c937d5bb246a..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_operations/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from ._operations import AuthoringClientOperationsMixin - -from ._patch import __all__ as _patch_all -from ._patch import * # pylint: disable=unused-wildcard-import -from ._patch import patch_sdk as _patch_sdk - -__all__ = [ - "AuthoringClientOperationsMixin", -] -__all__.extend([p for p in _patch_all if p not in __all__]) -_patch_sdk() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_operations/_operations.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_operations/_operations.py deleted file mode 100644 index 9bcbdea4df70..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_operations/_operations.py +++ /dev/null @@ -1,3352 +0,0 @@ -# pylint: disable=too-many-lines,too-many-statements -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- -from io import IOBase -import sys -from typing import Any, AsyncIterable, Callable, Dict, IO, List, Optional, TypeVar, Union, cast, overload -import urllib.parse - -from azure.core.async_paging import AsyncItemPaged, AsyncList -from azure.core.exceptions import ( - ClientAuthenticationError, - HttpResponseError, - ResourceExistsError, - ResourceNotFoundError, - ResourceNotModifiedError, - map_error, -) -from azure.core.pipeline import PipelineResponse -from azure.core.polling import AsyncLROPoller, AsyncNoPolling, AsyncPollingMethod -from azure.core.polling.async_base_polling import AsyncLROBasePolling -from azure.core.rest import AsyncHttpResponse, HttpRequest -from azure.core.tracing.decorator import distributed_trace -from azure.core.tracing.decorator_async import distributed_trace_async -from azure.core.utils import case_insensitive_dict - -from ..._operations._operations import ( - build_authoring_add_feedback_request, - build_authoring_create_project_request, - build_authoring_delete_project_request, - build_authoring_deploy_project_request, - build_authoring_export_request, - build_authoring_get_project_details_request, - build_authoring_import_assets_request, - build_authoring_list_deployments_request, - build_authoring_list_projects_request, - build_authoring_list_qnas_request, - build_authoring_list_sources_request, - build_authoring_list_synonyms_request, - build_authoring_update_qnas_request, - build_authoring_update_sources_request, - build_authoring_update_synonyms_request, -) -from .._vendor import AuthoringClientMixinABC - -if sys.version_info >= (3, 9): - from collections.abc import MutableMapping -else: - from typing import MutableMapping # type: ignore # pylint: disable=ungrouped-imports -JSON = MutableMapping[str, Any] # pylint: disable=unsubscriptable-object -T = TypeVar("T") -ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] - - -class AuthoringClientOperationsMixin(AuthoringClientMixinABC): # pylint: disable=too-many-public-methods - @distributed_trace - def list_projects( - self, *, top: Optional[int] = None, skip: Optional[int] = None, **kwargs: Any - ) -> AsyncIterable[JSON]: - # pylint: disable=line-too-long - """Gets all projects for a user. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/list-projects - for more information. - - :keyword top: The maximum number of resources to return from the collection. Default value is - None. - :paramtype top: int - :keyword skip: An offset into the collection of the first resource to be returned. Default - value is None. - :paramtype skip: int - :return: An iterator like instance of JSON object - :rtype: ~azure.core.async_paging.AsyncItemPaged[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Optional. Project creation - date-time. - "description": "str", # Optional. Description of the project. - "language": "str", # Optional. Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. - "lastDeployedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last deployment date-time. - "lastModifiedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last modified date-time. - "multilingualResource": bool, # Optional. Resource enabled for multiple - languages across projects or not. - "projectName": "str", # Optional. Name of the project. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - maxpagesize = kwargs.pop("maxpagesize", None) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_list_projects_request( - top=top, - skip=skip, - maxpagesize=maxpagesize, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) - - async def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return AsyncItemPaged(get_next, extract_data) - - @distributed_trace_async - async def get_project_details(self, project_name: str, **kwargs: Any) -> JSON: - # pylint: disable=line-too-long - """Get the requested project metadata. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/get-project-details - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :return: JSON object - :rtype: JSON - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Optional. Project creation - date-time. - "description": "str", # Optional. Description of the project. - "language": "str", # Optional. Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. - "lastDeployedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last deployment date-time. - "lastModifiedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last modified date-time. - "multilingualResource": bool, # Optional. Resource enabled for multiple - languages across projects or not. - "projectName": "str", # Optional. Name of the project. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - """ - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[JSON] = kwargs.pop("cls", None) - - _request = build_authoring_get_project_details_request( - project_name=project_name, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if response.content: - deserialized = response.json() - else: - deserialized = None - - if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore - - return cast(JSON, deserialized) # type: ignore - - @overload - async def create_project( - self, project_name: str, options: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> JSON: - # pylint: disable=line-too-long - """Create or update a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/create-project - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Parameters needed to create the project. Required. - :type options: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: JSON object - :rtype: JSON - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - options = { - "language": "str", # Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. Required. - "description": "str", # Optional. Description of the project. - "multilingualResource": bool, # Optional. Set to true to enable creating - knowledgebases in different languages for the same resource. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - - # response body for status code(s): 200, 201 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Optional. Project creation - date-time. - "description": "str", # Optional. Description of the project. - "language": "str", # Optional. Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. - "lastDeployedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last deployment date-time. - "lastModifiedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last modified date-time. - "multilingualResource": bool, # Optional. Resource enabled for multiple - languages across projects or not. - "projectName": "str", # Optional. Name of the project. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - """ - - @overload - async def create_project( - self, project_name: str, options: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> JSON: - # pylint: disable=line-too-long - """Create or update a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/create-project - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Parameters needed to create the project. Required. - :type options: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: JSON object - :rtype: JSON - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200, 201 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Optional. Project creation - date-time. - "description": "str", # Optional. Description of the project. - "language": "str", # Optional. Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. - "lastDeployedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last deployment date-time. - "lastModifiedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last modified date-time. - "multilingualResource": bool, # Optional. Resource enabled for multiple - languages across projects or not. - "projectName": "str", # Optional. Name of the project. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - """ - - @distributed_trace_async - async def create_project(self, project_name: str, options: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: - # pylint: disable=line-too-long - """Create or update a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/create-project - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Parameters needed to create the project. Is either a JSON type or a IO[bytes] - type. Required. - :type options: JSON or IO[bytes] - :return: JSON object - :rtype: JSON - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - options = { - "language": "str", # Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. Required. - "description": "str", # Optional. Description of the project. - "multilingualResource": bool, # Optional. Set to true to enable creating - knowledgebases in different languages for the same resource. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - - # response body for status code(s): 200, 201 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Optional. Project creation - date-time. - "description": "str", # Optional. Description of the project. - "language": "str", # Optional. Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for Spanish - etc. If not set, use "en" for English as default. - "lastDeployedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last deployment date-time. - "lastModifiedDateTime": "2020-02-20 00:00:00", # Optional. Represents the - project last modified date-time. - "multilingualResource": bool, # Optional. Resource enabled for multiple - languages across projects or not. - "projectName": "str", # Optional. Name of the project. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response when no - good match is found in the knowledge base. - } - } - """ - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(options, (IOBase, bytes)): - _content = options - else: - _json = options - - _request = build_authoring_create_project_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 201]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 201: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore - - return cast(JSON, deserialized) # type: ignore - - async def _delete_project_initial( # pylint: disable=inconsistent-return-statements - self, project_name: str, **kwargs: Any - ) -> None: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[None] = kwargs.pop("cls", None) - - _request = build_authoring_delete_project_request( - project_name=project_name, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [202]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - response_headers = {} - response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location")) - - if cls: - return cls(pipeline_response, None, response_headers) # type: ignore - - @distributed_trace_async - async def begin_delete_project(self, project_name: str, **kwargs: Any) -> AsyncLROPoller[None]: - """Delete the project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/delete-project - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :return: An instance of AsyncLROPoller that returns None - :rtype: ~azure.core.polling.AsyncLROPoller[None] - :raises ~azure.core.exceptions.HttpResponseError: - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[None] = kwargs.pop("cls", None) - polling: Union[bool, AsyncPollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = await self._delete_project_initial( # type: ignore - project_name=project_name, cls=lambda x, y, z: x, headers=_headers, params=_params, **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): # pylint: disable=inconsistent-return-statements - if cls: - return cls(pipeline_response, None, {}) # type: ignore - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: AsyncPollingMethod = cast( - AsyncPollingMethod, - AsyncLROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs), - ) - elif polling is False: - polling_method = cast(AsyncPollingMethod, AsyncNoPolling()) - else: - polling_method = polling - if cont_token: - return AsyncLROPoller[None].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return AsyncLROPoller[None](self._client, raw_result, get_long_running_output, polling_method) # type: ignore - - async def _export_initial( - self, project_name: str, *, file_format: str = "json", asset_kind: Optional[str] = None, **kwargs: Any - ) -> Optional[JSON]: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) - - _request = build_authoring_export_request( - project_name=project_name, - file_format=file_format, - asset_kind=asset_kind, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 202]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - deserialized = None - response_headers = {} - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 202: - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) - - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore - - @distributed_trace_async - async def begin_export( - self, project_name: str, *, file_format: str = "json", asset_kind: Optional[str] = None, **kwargs: Any - ) -> AsyncLROPoller[JSON]: - # pylint: disable=line-too-long - """Export project metadata and assets. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/export - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :keyword file_format: Knowledge base Import or Export format. Known values are: "json", "tsv", - and "excel". Default value is "json". - :paramtype file_format: str - :keyword asset_kind: Kind of the asset of the project. Known values are: "qnas" and "synonyms". - Default value is None. - :paramtype asset_kind: str - :return: An instance of AsyncLROPoller that returns JSON object - :rtype: ~azure.core.polling.AsyncLROPoller[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Required. - "jobId": "str", # Required. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Required. - "resultUrl": "str", # URL to download the result of the Export Job. - Required. - "status": "str", # Job Status. Required. Known values are: "notStarted", - "running", "succeeded", "failed", "cancelled", "cancelling", and - "partiallyCompleted". - "errors": [ - { - "code": "str", # One of a server-defined set of error codes. - Required. Known values are: "InvalidRequest", "InvalidArgument", - "Unauthorized", "Forbidden", "NotFound", "ProjectNotFound", - "OperationNotFound", "AzureCognitiveSearchNotFound", - "AzureCognitiveSearchIndexNotFound", "TooManyRequests", - "AzureCognitiveSearchThrottling", - "AzureCognitiveSearchIndexLimitReached", "InternalServerError", and - "ServiceUnavailable". - "message": "str", # A human-readable representation of the - error. Required. - "details": [ - ... - ], - "innererror": { - "code": "str", # One of a server-defined set of - error codes. Required. Known values are: "InvalidRequest", - "InvalidParameterValue", "KnowledgeBaseNotFound", - "AzureCognitiveSearchNotFound", "AzureCognitiveSearchThrottling", and - "ExtractionFailure". - "message": "str", # Error message. Required. - "details": { - "str": "str" # Optional. Error details. - }, - "innererror": ..., - "target": "str" # Optional. Error target. - }, - "target": "str" # Optional. The target of the error. - } - ], - "expirationDateTime": "2020-02-20 00:00:00" # Optional. - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[JSON] = kwargs.pop("cls", None) - polling: Union[bool, AsyncPollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = await self._export_initial( - project_name=project_name, - file_format=file_format, - asset_kind=asset_kind, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): - response = pipeline_response.http_response - if response.content: - deserialized = response.json() - else: - deserialized = None - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore - return deserialized - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: AsyncPollingMethod = cast( - AsyncPollingMethod, - AsyncLROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs), - ) - elif polling is False: - polling_method = cast(AsyncPollingMethod, AsyncNoPolling()) - else: - polling_method = polling - if cont_token: - return AsyncLROPoller[JSON].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return AsyncLROPoller[JSON](self._client, raw_result, get_long_running_output, polling_method) # type: ignore - - async def _import_assets_initial( - self, - project_name: str, - options: Optional[Union[JSON, IO[bytes]]] = None, - *, - file_format: str = "json", - asset_kind: Optional[str] = None, - **kwargs: Any - ) -> Optional[JSON]: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(options, (IOBase, bytes)): - _content = options - else: - if options is not None: - _json = options - else: - _json = None - - _request = build_authoring_import_assets_request( - project_name=project_name, - file_format=file_format, - asset_kind=asset_kind, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 202]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - deserialized = None - response_headers = {} - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 202: - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) - - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore - - @overload - async def begin_import_assets( - self, - project_name: str, - options: Optional[JSON] = None, - *, - file_format: str = "json", - asset_kind: Optional[str] = None, - content_type: str = "application/json", - **kwargs: Any - ) -> AsyncLROPoller[JSON]: - # pylint: disable=line-too-long - """Import project assets. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/import - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Project assets the needs to be imported. Default value is None. - :type options: JSON - :keyword file_format: Knowledge base Import or Export format. Known values are: "json", "tsv", - and "excel". Default value is "json". - :paramtype file_format: str - :keyword asset_kind: Kind of the asset of the project. Known values are: "qnas" and "synonyms". - Default value is None. - :paramtype asset_kind: str - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of AsyncLROPoller that returns JSON object - :rtype: ~azure.core.polling.AsyncLROPoller[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - options = { - "assets": { - "qnas": [ - { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. - Question chosen as the head of suggested questions cluster by - Active Learning clustering algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": - 0, # Optional. The number of times the question was - suggested automatically by the Active Learning - algorithm. - "question": "str", # - Optional. Question suggested by the Active Learning - feature. - "userSuggestedCount": - 0 # Optional. The number of times the question was - suggested explicitly by the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark - if a prompt is relevant only with a previous question or not. If - true, do not include this QnA as answer for queries without - context; otherwise, ignores context and includes this QnA in - answers. - "prompts": [ - { - "displayOrder": 0, # - Optional. Index of the prompt. It is used for ordering of - the prompts. - "displayText": "str", # - Optional. Text displayed to represent a follow up - question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question - chosen as the head of suggested questions - cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. - The number of times the question was - suggested automatically by the Active - Learning algorithm. - "question": "str", # Optional. - Question suggested by the Active - Learning feature. - "userSuggestedCount": 0 # Optional. - The number of times the question was - suggested explicitly by the user. - } - ] - } - ], - "answer": "str", # - Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. - Unique ID for the QnA. - "metadata": { - "str": "str" - # Optional. Metadata associated with the answer, - useful to categorize or filter question answers. - }, - "questions": [ - "str" # - Optional. List of questions associated with the - answer. - ], - "source": "str" # - Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of - the QnA corresponding to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # - Optional. Date-time when the QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated - with the answer, useful to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str", # Optional. Source from which QnA - was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - "sourceDisplayName": "str" # Optional. Friendly name - of the Source. - } - ], - "synonyms": [ - { - "alterations": [ - "str" # Collection of word alterations. - Required. - ] - } - ] - }, - "fileUri": "str", # Optional. Import data File URI. - "metadata": { - "language": "str", # Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for - Spanish etc. If not set, use "en" for English as default. Required. - "description": "str", # Optional. Description of the project. - "multilingualResource": bool, # Optional. Set to true to enable - creating knowledgebases in different languages for the same resource. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response - when no good match is found in the knowledge base. - } - } - } - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Required. - "jobId": "str", # Required. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Required. - "status": "str", # Job Status. Required. Known values are: "notStarted", - "running", "succeeded", "failed", "cancelled", "cancelling", and - "partiallyCompleted". - "errors": [ - { - "code": "str", # One of a server-defined set of error codes. - Required. Known values are: "InvalidRequest", "InvalidArgument", - "Unauthorized", "Forbidden", "NotFound", "ProjectNotFound", - "OperationNotFound", "AzureCognitiveSearchNotFound", - "AzureCognitiveSearchIndexNotFound", "TooManyRequests", - "AzureCognitiveSearchThrottling", - "AzureCognitiveSearchIndexLimitReached", "InternalServerError", and - "ServiceUnavailable". - "message": "str", # A human-readable representation of the - error. Required. - "details": [ - ... - ], - "innererror": { - "code": "str", # One of a server-defined set of - error codes. Required. Known values are: "InvalidRequest", - "InvalidParameterValue", "KnowledgeBaseNotFound", - "AzureCognitiveSearchNotFound", "AzureCognitiveSearchThrottling", and - "ExtractionFailure". - "message": "str", # Error message. Required. - "details": { - "str": "str" # Optional. Error details. - }, - "innererror": ..., - "target": "str" # Optional. Error target. - }, - "target": "str" # Optional. The target of the error. - } - ], - "expirationDateTime": "2020-02-20 00:00:00" # Optional. - } - """ - - @overload - async def begin_import_assets( - self, - project_name: str, - options: Optional[IO[bytes]] = None, - *, - file_format: str = "json", - asset_kind: Optional[str] = None, - content_type: str = "application/json", - **kwargs: Any - ) -> AsyncLROPoller[JSON]: - # pylint: disable=line-too-long - """Import project assets. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/import - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Project assets the needs to be imported. Default value is None. - :type options: IO[bytes] - :keyword file_format: Knowledge base Import or Export format. Known values are: "json", "tsv", - and "excel". Default value is "json". - :paramtype file_format: str - :keyword asset_kind: Kind of the asset of the project. Known values are: "qnas" and "synonyms". - Default value is None. - :paramtype asset_kind: str - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of AsyncLROPoller that returns JSON object - :rtype: ~azure.core.polling.AsyncLROPoller[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Required. - "jobId": "str", # Required. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Required. - "status": "str", # Job Status. Required. Known values are: "notStarted", - "running", "succeeded", "failed", "cancelled", "cancelling", and - "partiallyCompleted". - "errors": [ - { - "code": "str", # One of a server-defined set of error codes. - Required. Known values are: "InvalidRequest", "InvalidArgument", - "Unauthorized", "Forbidden", "NotFound", "ProjectNotFound", - "OperationNotFound", "AzureCognitiveSearchNotFound", - "AzureCognitiveSearchIndexNotFound", "TooManyRequests", - "AzureCognitiveSearchThrottling", - "AzureCognitiveSearchIndexLimitReached", "InternalServerError", and - "ServiceUnavailable". - "message": "str", # A human-readable representation of the - error. Required. - "details": [ - ... - ], - "innererror": { - "code": "str", # One of a server-defined set of - error codes. Required. Known values are: "InvalidRequest", - "InvalidParameterValue", "KnowledgeBaseNotFound", - "AzureCognitiveSearchNotFound", "AzureCognitiveSearchThrottling", and - "ExtractionFailure". - "message": "str", # Error message. Required. - "details": { - "str": "str" # Optional. Error details. - }, - "innererror": ..., - "target": "str" # Optional. Error target. - }, - "target": "str" # Optional. The target of the error. - } - ], - "expirationDateTime": "2020-02-20 00:00:00" # Optional. - } - """ - - @distributed_trace_async - async def begin_import_assets( - self, - project_name: str, - options: Optional[Union[JSON, IO[bytes]]] = None, - *, - file_format: str = "json", - asset_kind: Optional[str] = None, - **kwargs: Any - ) -> AsyncLROPoller[JSON]: - # pylint: disable=line-too-long - """Import project assets. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/import - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param options: Project assets the needs to be imported. Is either a JSON type or a IO[bytes] - type. Default value is None. - :type options: JSON or IO[bytes] - :keyword file_format: Knowledge base Import or Export format. Known values are: "json", "tsv", - and "excel". Default value is "json". - :paramtype file_format: str - :keyword asset_kind: Kind of the asset of the project. Known values are: "qnas" and "synonyms". - Default value is None. - :paramtype asset_kind: str - :return: An instance of AsyncLROPoller that returns JSON object - :rtype: ~azure.core.polling.AsyncLROPoller[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - options = { - "assets": { - "qnas": [ - { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. - Question chosen as the head of suggested questions cluster by - Active Learning clustering algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": - 0, # Optional. The number of times the question was - suggested automatically by the Active Learning - algorithm. - "question": "str", # - Optional. Question suggested by the Active Learning - feature. - "userSuggestedCount": - 0 # Optional. The number of times the question was - suggested explicitly by the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark - if a prompt is relevant only with a previous question or not. If - true, do not include this QnA as answer for queries without - context; otherwise, ignores context and includes this QnA in - answers. - "prompts": [ - { - "displayOrder": 0, # - Optional. Index of the prompt. It is used for ordering of - the prompts. - "displayText": "str", # - Optional. Text displayed to represent a follow up - question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question - chosen as the head of suggested questions - cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. - The number of times the question was - suggested automatically by the Active - Learning algorithm. - "question": "str", # Optional. - Question suggested by the Active - Learning feature. - "userSuggestedCount": 0 # Optional. - The number of times the question was - suggested explicitly by the user. - } - ] - } - ], - "answer": "str", # - Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. - Unique ID for the QnA. - "metadata": { - "str": "str" - # Optional. Metadata associated with the answer, - useful to categorize or filter question answers. - }, - "questions": [ - "str" # - Optional. List of questions associated with the - answer. - ], - "source": "str" # - Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of - the QnA corresponding to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # - Optional. Date-time when the QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated - with the answer, useful to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str", # Optional. Source from which QnA - was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - "sourceDisplayName": "str" # Optional. Friendly name - of the Source. - } - ], - "synonyms": [ - { - "alterations": [ - "str" # Collection of word alterations. - Required. - ] - } - ] - }, - "fileUri": "str", # Optional. Import data File URI. - "metadata": { - "language": "str", # Language of the text records. This is BCP-47 - representation of a language. For example, use "en" for English; "es" for - Spanish etc. If not set, use "en" for English as default. Required. - "description": "str", # Optional. Description of the project. - "multilingualResource": bool, # Optional. Set to true to enable - creating knowledgebases in different languages for the same resource. - "settings": { - "defaultAnswer": "str" # Optional. Default Answer response - when no good match is found in the knowledge base. - } - } - } - - # response body for status code(s): 200 - response == { - "createdDateTime": "2020-02-20 00:00:00", # Required. - "jobId": "str", # Required. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Required. - "status": "str", # Job Status. Required. Known values are: "notStarted", - "running", "succeeded", "failed", "cancelled", "cancelling", and - "partiallyCompleted". - "errors": [ - { - "code": "str", # One of a server-defined set of error codes. - Required. Known values are: "InvalidRequest", "InvalidArgument", - "Unauthorized", "Forbidden", "NotFound", "ProjectNotFound", - "OperationNotFound", "AzureCognitiveSearchNotFound", - "AzureCognitiveSearchIndexNotFound", "TooManyRequests", - "AzureCognitiveSearchThrottling", - "AzureCognitiveSearchIndexLimitReached", "InternalServerError", and - "ServiceUnavailable". - "message": "str", # A human-readable representation of the - error. Required. - "details": [ - ... - ], - "innererror": { - "code": "str", # One of a server-defined set of - error codes. Required. Known values are: "InvalidRequest", - "InvalidParameterValue", "KnowledgeBaseNotFound", - "AzureCognitiveSearchNotFound", "AzureCognitiveSearchThrottling", and - "ExtractionFailure". - "message": "str", # Error message. Required. - "details": { - "str": "str" # Optional. Error details. - }, - "innererror": ..., - "target": "str" # Optional. Error target. - }, - "target": "str" # Optional. The target of the error. - } - ], - "expirationDateTime": "2020-02-20 00:00:00" # Optional. - } - """ - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[JSON] = kwargs.pop("cls", None) - polling: Union[bool, AsyncPollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = await self._import_assets_initial( - project_name=project_name, - options=options, - file_format=file_format, - asset_kind=asset_kind, - content_type=content_type, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): - response = pipeline_response.http_response - if response.content: - deserialized = response.json() - else: - deserialized = None - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore - return deserialized - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: AsyncPollingMethod = cast( - AsyncPollingMethod, - AsyncLROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs), - ) - elif polling is False: - polling_method = cast(AsyncPollingMethod, AsyncNoPolling()) - else: - polling_method = polling - if cont_token: - return AsyncLROPoller[JSON].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return AsyncLROPoller[JSON](self._client, raw_result, get_long_running_output, polling_method) # type: ignore - - async def _deploy_project_initial(self, project_name: str, deployment_name: str, **kwargs: Any) -> Optional[JSON]: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) - - _request = build_authoring_deploy_project_request( - project_name=project_name, - deployment_name=deployment_name, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 202]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - deserialized = None - response_headers = {} - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 202: - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) - - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore - - @distributed_trace_async - async def begin_deploy_project( - self, project_name: str, deployment_name: str, **kwargs: Any - ) -> AsyncLROPoller[JSON]: - """Deploy project to production. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/deploy-project - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param deployment_name: The name of the specific deployment of the project to use. Required. - :type deployment_name: str - :return: An instance of AsyncLROPoller that returns JSON object - :rtype: ~azure.core.polling.AsyncLROPoller[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "deploymentName": "str", # Optional. Name of the deployment. - "lastDeployedDateTime": "2020-02-20 00:00:00" # Optional. Represents the - project last deployment date-time. - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[JSON] = kwargs.pop("cls", None) - polling: Union[bool, AsyncPollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = await self._deploy_project_initial( - project_name=project_name, - deployment_name=deployment_name, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): - response = pipeline_response.http_response - if response.content: - deserialized = response.json() - else: - deserialized = None - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore - return deserialized - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: AsyncPollingMethod = cast( - AsyncPollingMethod, - AsyncLROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs), - ) - elif polling is False: - polling_method = cast(AsyncPollingMethod, AsyncNoPolling()) - else: - polling_method = polling - if cont_token: - return AsyncLROPoller[JSON].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return AsyncLROPoller[JSON](self._client, raw_result, get_long_running_output, polling_method) # type: ignore - - @distributed_trace - def list_deployments( - self, project_name: str, *, top: Optional[int] = None, skip: Optional[int] = None, **kwargs: Any - ) -> AsyncIterable[JSON]: - """List all deployments of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/list-deployments - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :keyword top: The maximum number of resources to return from the collection. Default value is - None. - :paramtype top: int - :keyword skip: An offset into the collection of the first resource to be returned. Default - value is None. - :paramtype skip: int - :return: An iterator like instance of JSON object - :rtype: ~azure.core.async_paging.AsyncItemPaged[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "deploymentName": "str", # Optional. Name of the deployment. - "lastDeployedDateTime": "2020-02-20 00:00:00" # Optional. Represents the - project last deployment date-time. - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - maxpagesize = kwargs.pop("maxpagesize", None) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_list_deployments_request( - project_name=project_name, - top=top, - skip=skip, - maxpagesize=maxpagesize, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) - - async def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return AsyncItemPaged(get_next, extract_data) - - @distributed_trace - def list_synonyms( - self, project_name: str, *, top: Optional[int] = None, skip: Optional[int] = None, **kwargs: Any - ) -> AsyncIterable[JSON]: - """Gets all the synonyms of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/get-synonyms - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :keyword top: The maximum number of resources to return from the collection. Default value is - None. - :paramtype top: int - :keyword skip: An offset into the collection of the first resource to be returned. Default - value is None. - :paramtype skip: int - :return: An iterator like instance of JSON object - :rtype: ~azure.core.async_paging.AsyncItemPaged[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "alterations": [ - "str" # Collection of word alterations. Required. - ] - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - maxpagesize = kwargs.pop("maxpagesize", None) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_list_synonyms_request( - project_name=project_name, - top=top, - skip=skip, - maxpagesize=maxpagesize, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) - - async def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return AsyncItemPaged(get_next, extract_data) - - @overload - async def update_synonyms( # pylint: disable=inconsistent-return-statements - self, project_name: str, synonyms: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> None: - """Updates all the synonyms of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-synonyms - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param synonyms: All the synonyms of a project. Required. - :type synonyms: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - synonyms = { - "nextLink": "str", # Optional. - "value": [ - { - "alterations": [ - "str" # Collection of word alterations. Required. - ] - } - ] - } - """ - - @overload - async def update_synonyms( # pylint: disable=inconsistent-return-statements - self, project_name: str, synonyms: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> None: - """Updates all the synonyms of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-synonyms - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param synonyms: All the synonyms of a project. Required. - :type synonyms: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @distributed_trace_async - async def update_synonyms( # pylint: disable=inconsistent-return-statements - self, project_name: str, synonyms: Union[JSON, IO[bytes]], **kwargs: Any - ) -> None: - """Updates all the synonyms of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-synonyms - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param synonyms: All the synonyms of a project. Is either a JSON type or a IO[bytes] type. - Required. - :type synonyms: JSON or IO[bytes] - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - synonyms = { - "nextLink": "str", # Optional. - "value": [ - { - "alterations": [ - "str" # Collection of word alterations. Required. - ] - } - ] - } - """ - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[None] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(synonyms, (IOBase, bytes)): - _content = synonyms - else: - _json = synonyms - - _request = build_authoring_update_synonyms_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [204]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if cls: - return cls(pipeline_response, None, {}) # type: ignore - - @distributed_trace - def list_sources( - self, project_name: str, *, top: Optional[int] = None, skip: Optional[int] = None, **kwargs: Any - ) -> AsyncIterable[JSON]: - # pylint: disable=line-too-long - """Gets all the sources of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/get-sources - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :keyword top: The maximum number of resources to return from the collection. Default value is - None. - :paramtype top: int - :keyword skip: An offset into the collection of the first resource to be returned. Default - value is None. - :paramtype skip: int - :return: An iterator like instance of JSON object - :rtype: ~azure.core.async_paging.AsyncItemPaged[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "sourceKind": "str", # Supported source types. Required. Known values are: - "file" and "url". - "sourceUri": "str", # URI location for the file or url. Required. - "contentStructureKind": "str", # Optional. Content structure type for - sources. "unstructured" - "displayName": "str", # Optional. Friendly name of the Source. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "source": "str" # Optional. Unique source identifier. Name of the file if - it's a 'file' source; otherwise, the complete URL if it's a 'url' source. - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - maxpagesize = kwargs.pop("maxpagesize", None) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_list_sources_request( - project_name=project_name, - top=top, - skip=skip, - maxpagesize=maxpagesize, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) - - async def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return AsyncItemPaged(get_next, extract_data) - - async def _update_sources_initial( - self, project_name: str, sources: Union[List[JSON], IO[bytes]], **kwargs: Any - ) -> Optional[JSON]: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(sources, (IOBase, bytes)): - _content = sources - else: - _json = sources - - _request = build_authoring_update_sources_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 202]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - deserialized = None - response_headers = {} - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 202: - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) - - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore - - @overload - async def begin_update_sources( - self, project_name: str, sources: List[JSON], *, content_type: str = "application/json", **kwargs: Any - ) -> AsyncLROPoller[AsyncIterable[JSON]]: - # pylint: disable=line-too-long - """Updates the sources of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-sources - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param sources: Update sources parameters of a project. Required. - :type sources: list[JSON] - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.AsyncLROPoller[~azure.core.async_paging.AsyncItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - sources = [ - { - "op": "str", # Update operation type for assets. Required. Known - values are: "add", "delete", and "replace". - "value": { - "sourceKind": "str", # Supported source types. Required. - Known values are: "file" and "url". - "sourceUri": "str", # URI location for the file or url. - Required. - "contentStructureKind": "str", # Optional. Content structure - type for sources. "unstructured" - "displayName": "str", # Optional. Friendly name of the - Source. - "refresh": bool, # Optional. Boolean flag used to refresh - data from the Source. - "source": "str" # Optional. Unique source identifier. Name - of the file if it's a 'file' source; otherwise, the complete URL if it's - a 'url' source. - } - } - ] - - # response body for status code(s): 200, 202 - response == { - "sourceKind": "str", # Supported source types. Required. Known values are: - "file" and "url". - "sourceUri": "str", # URI location for the file or url. Required. - "contentStructureKind": "str", # Optional. Content structure type for - sources. "unstructured" - "displayName": "str", # Optional. Friendly name of the Source. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "source": "str" # Optional. Unique source identifier. Name of the file if - it's a 'file' source; otherwise, the complete URL if it's a 'url' source. - } - """ - - @overload - async def begin_update_sources( - self, project_name: str, sources: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> AsyncLROPoller[AsyncIterable[JSON]]: - # pylint: disable=line-too-long - """Updates the sources of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-sources - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param sources: Update sources parameters of a project. Required. - :type sources: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.AsyncLROPoller[~azure.core.async_paging.AsyncItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200, 202 - response == { - "sourceKind": "str", # Supported source types. Required. Known values are: - "file" and "url". - "sourceUri": "str", # URI location for the file or url. Required. - "contentStructureKind": "str", # Optional. Content structure type for - sources. "unstructured" - "displayName": "str", # Optional. Friendly name of the Source. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "source": "str" # Optional. Unique source identifier. Name of the file if - it's a 'file' source; otherwise, the complete URL if it's a 'url' source. - } - """ - - @distributed_trace_async - async def begin_update_sources( - self, project_name: str, sources: Union[List[JSON], IO[bytes]], **kwargs: Any - ) -> AsyncLROPoller[AsyncIterable[JSON]]: - # pylint: disable=line-too-long - """Updates the sources of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-sources - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param sources: Update sources parameters of a project. Is either a [JSON] type or a IO[bytes] - type. Required. - :type sources: list[JSON] or IO[bytes] - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.AsyncLROPoller[~azure.core.async_paging.AsyncItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200, 202 - response == { - "sourceKind": "str", # Supported source types. Required. Known values are: - "file" and "url". - "sourceUri": "str", # URI location for the file or url. Required. - "contentStructureKind": "str", # Optional. Content structure type for - sources. "unstructured" - "displayName": "str", # Optional. Friendly name of the Source. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "source": "str" # Optional. Unique source identifier. Name of the file if - it's a 'file' source; otherwise, the complete URL if it's a 'url' source. - } - """ - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(sources, (IOBase, bytes)): - _content = sources - else: - _json = sources - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_update_sources_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) - - async def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - polling: Union[bool, AsyncPollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = await self._update_sources_initial( - project_name=project_name, - sources=sources, - content_type=content_type, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): - async def internal_get_next(next_link=None): - if next_link is None: - return pipeline_response - return await get_next(next_link) - - return AsyncItemPaged(internal_get_next, extract_data) - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: AsyncPollingMethod = cast( - AsyncPollingMethod, - AsyncLROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs), - ) - elif polling is False: - polling_method = cast(AsyncPollingMethod, AsyncNoPolling()) - else: - polling_method = polling - if cont_token: - return AsyncLROPoller[AsyncIterable[JSON]].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return AsyncLROPoller[AsyncIterable[JSON]]( - self._client, raw_result, get_long_running_output, polling_method # type: ignore - ) - - @distributed_trace - def list_qnas( - self, - project_name: str, - *, - source: Optional[str] = None, - top: Optional[int] = None, - skip: Optional[int] = None, - **kwargs: Any - ) -> AsyncIterable[JSON]: - # pylint: disable=line-too-long - """Gets all the QnAs of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/get-qnas - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :keyword source: Source of the QnA. Default value is None. - :paramtype source: str - :keyword top: The maximum number of resources to return from the collection. Default value is - None. - :paramtype top: int - :keyword skip: An offset into the collection of the first resource to be returned. Default - value is None. - :paramtype skip: int - :return: An iterator like instance of JSON object - :rtype: ~azure.core.async_paging.AsyncItemPaged[JSON] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question chosen as the - head of suggested questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The - number of times the question was suggested automatically by the - Active Learning algorithm. - "question": "str", # Optional. Question - suggested by the Active Learning feature. - "userSuggestedCount": 0 # Optional. The - number of times the question was suggested explicitly by the - user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark if a prompt is relevant - only with a previous question or not. If true, do not include this QnA as - answer for queries without context; otherwise, ignores context and includes - this QnA in answers. - "prompts": [ - { - "displayOrder": 0, # Optional. Index of the prompt. - It is used for ordering of the prompts. - "displayText": "str", # Optional. Text displayed to - represent a follow up question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # - Optional. Question chosen as the head of suggested - questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The number - of times the question was suggested automatically - by the Active Learning algorithm. - "question": - "str", # Optional. Question suggested by the - Active Learning feature. - "userSuggestedCount": 0 # Optional. The number - of times the question was suggested explicitly by - the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. Unique ID for the QnA. - "metadata": { - "str": "str" # Optional. Metadata - associated with the answer, useful to categorize or filter - question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str" # Optional. Source from - which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of the QnA corresponding - to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated with the answer, useful - to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions associated with the answer. - ], - "source": "str" # Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs . - } - """ - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - maxpagesize = kwargs.pop("maxpagesize", None) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_list_qnas_request( - project_name=project_name, - source=source, - top=top, - skip=skip, - maxpagesize=maxpagesize, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) - - async def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - return AsyncItemPaged(get_next, extract_data) - - async def _update_qnas_initial( - self, project_name: str, qnas: Union[List[JSON], IO[bytes]], **kwargs: Any - ) -> Optional[JSON]: - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(qnas, (IOBase, bytes)): - _content = qnas - else: - _json = qnas - - _request = build_authoring_update_qnas_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200, 202]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - deserialized = None - response_headers = {} - if response.status_code == 200: - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 202: - response_headers["Operation-Location"] = self._deserialize( - "str", response.headers.get("Operation-Location") - ) - - if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore - - return deserialized # type: ignore - - @overload - async def begin_update_qnas( - self, project_name: str, qnas: List[JSON], *, content_type: str = "application/json", **kwargs: Any - ) -> AsyncLROPoller[AsyncIterable[JSON]]: - # pylint: disable=line-too-long - """Updates the QnAs of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-qnas - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param qnas: Update QnAs parameters of a project. Required. - :type qnas: list[JSON] - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.AsyncLROPoller[~azure.core.async_paging.AsyncItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - qnas = [ - { - "op": "str", # Update operation type for assets. Required. Known - values are: "add", "delete", and "replace". - "value": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question - chosen as the head of suggested questions cluster by Active - Learning clustering algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # - Optional. The number of times the question was suggested - automatically by the Active Learning algorithm. - "question": "str", # - Optional. Question suggested by the Active Learning - feature. - "userSuggestedCount": 0 # - Optional. The number of times the question was suggested - explicitly by the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark if a - prompt is relevant only with a previous question or not. If true, do - not include this QnA as answer for queries without context; - otherwise, ignores context and includes this QnA in answers. - "prompts": [ - { - "displayOrder": 0, # Optional. Index - of the prompt. It is used for ordering of the prompts. - "displayText": "str", # Optional. - Text displayed to represent a follow up question prompt. - "qna": ..., - "qnaId": 0 # Optional. ID of the QnA - corresponding to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "metadata": { - "str": "str" # Optional. Metadata associated with - the answer, useful to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions associated with - the answer. - ], - "source": "str" # Optional. Source from which QnA was - indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs . - } - } - ] - - # response body for status code(s): 200, 202 - response == { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question chosen as the - head of suggested questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The - number of times the question was suggested automatically by the - Active Learning algorithm. - "question": "str", # Optional. Question - suggested by the Active Learning feature. - "userSuggestedCount": 0 # Optional. The - number of times the question was suggested explicitly by the - user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark if a prompt is relevant - only with a previous question or not. If true, do not include this QnA as - answer for queries without context; otherwise, ignores context and includes - this QnA in answers. - "prompts": [ - { - "displayOrder": 0, # Optional. Index of the prompt. - It is used for ordering of the prompts. - "displayText": "str", # Optional. Text displayed to - represent a follow up question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # - Optional. Question chosen as the head of suggested - questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The number - of times the question was suggested automatically - by the Active Learning algorithm. - "question": - "str", # Optional. Question suggested by the - Active Learning feature. - "userSuggestedCount": 0 # Optional. The number - of times the question was suggested explicitly by - the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. Unique ID for the QnA. - "metadata": { - "str": "str" # Optional. Metadata - associated with the answer, useful to categorize or filter - question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str" # Optional. Source from - which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of the QnA corresponding - to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated with the answer, useful - to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions associated with the answer. - ], - "source": "str" # Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs . - } - """ - - @overload - async def begin_update_qnas( - self, project_name: str, qnas: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> AsyncLROPoller[AsyncIterable[JSON]]: - # pylint: disable=line-too-long - """Updates the QnAs of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-qnas - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param qnas: Update QnAs parameters of a project. Required. - :type qnas: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.AsyncLROPoller[~azure.core.async_paging.AsyncItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200, 202 - response == { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question chosen as the - head of suggested questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The - number of times the question was suggested automatically by the - Active Learning algorithm. - "question": "str", # Optional. Question - suggested by the Active Learning feature. - "userSuggestedCount": 0 # Optional. The - number of times the question was suggested explicitly by the - user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark if a prompt is relevant - only with a previous question or not. If true, do not include this QnA as - answer for queries without context; otherwise, ignores context and includes - this QnA in answers. - "prompts": [ - { - "displayOrder": 0, # Optional. Index of the prompt. - It is used for ordering of the prompts. - "displayText": "str", # Optional. Text displayed to - represent a follow up question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # - Optional. Question chosen as the head of suggested - questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The number - of times the question was suggested automatically - by the Active Learning algorithm. - "question": - "str", # Optional. Question suggested by the - Active Learning feature. - "userSuggestedCount": 0 # Optional. The number - of times the question was suggested explicitly by - the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. Unique ID for the QnA. - "metadata": { - "str": "str" # Optional. Metadata - associated with the answer, useful to categorize or filter - question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str" # Optional. Source from - which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of the QnA corresponding - to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated with the answer, useful - to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions associated with the answer. - ], - "source": "str" # Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs . - } - """ - - @distributed_trace_async - async def begin_update_qnas( - self, project_name: str, qnas: Union[List[JSON], IO[bytes]], **kwargs: Any - ) -> AsyncLROPoller[AsyncIterable[JSON]]: - # pylint: disable=line-too-long - """Updates the QnAs of a project. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/update-qnas - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param qnas: Update QnAs parameters of a project. Is either a [JSON] type or a IO[bytes] type. - Required. - :type qnas: list[JSON] or IO[bytes] - :return: An instance of LROPoller that returns an iterator like instance of JSON object - :rtype: ~azure.core.polling.AsyncLROPoller[~azure.core.async_paging.AsyncItemPaged[JSON]] - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200, 202 - response == { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # Optional. Question chosen as the - head of suggested questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The - number of times the question was suggested automatically by the - Active Learning algorithm. - "question": "str", # Optional. Question - suggested by the Active Learning feature. - "userSuggestedCount": 0 # Optional. The - number of times the question was suggested explicitly by the - user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": { - "isContextOnly": bool, # Optional. To mark if a prompt is relevant - only with a previous question or not. If true, do not include this QnA as - answer for queries without context; otherwise, ignores context and includes - this QnA in answers. - "prompts": [ - { - "displayOrder": 0, # Optional. Index of the prompt. - It is used for ordering of the prompts. - "displayText": "str", # Optional. Text displayed to - represent a follow up question prompt. - "qna": { - "activeLearningSuggestions": [ - { - "clusterHead": "str", # - Optional. Question chosen as the head of suggested - questions cluster by Active Learning clustering - algorithm. - "suggestedQuestions": [ - { - "autoSuggestedCount": 0, # Optional. The number - of times the question was suggested automatically - by the Active Learning algorithm. - "question": - "str", # Optional. Question suggested by the - Active Learning feature. - "userSuggestedCount": 0 # Optional. The number - of times the question was suggested explicitly by - the user. - } - ] - } - ], - "answer": "str", # Optional. Answer text. - "dialog": ..., - "id": 0, # Optional. Unique ID for the QnA. - "metadata": { - "str": "str" # Optional. Metadata - associated with the answer, useful to categorize or filter - question answers. - }, - "questions": [ - "str" # Optional. List of questions - associated with the answer. - ], - "source": "str" # Optional. Source from - which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs - . - }, - "qnaId": 0 # Optional. ID of the QnA corresponding - to the prompt. - } - ] - }, - "id": 0, # Optional. Unique ID for the QnA. - "lastUpdatedDateTime": "2020-02-20 00:00:00", # Optional. Date-time when the - QnA was last updated. - "metadata": { - "str": "str" # Optional. Metadata associated with the answer, useful - to categorize or filter question answers. - }, - "questions": [ - "str" # Optional. List of questions associated with the answer. - ], - "source": "str" # Optional. Source from which QnA was indexed e.g. - https://learn.microsoft.com/azure/cognitive-services/QnAMaker/FAQs . - } - """ - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[JSON] = kwargs.pop("cls", None) - - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(qnas, (IOBase, bytes)): - _content = qnas - else: - _json = qnas - - def prepare_request(next_link=None): - if not next_link: - - _request = build_authoring_update_qnas_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - else: - # make call to next link with the client's api-version - _parsed_next_link = urllib.parse.urlparse(next_link) - _next_request_params = case_insensitive_dict( - { - key: [urllib.parse.quote(v) for v in value] - for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() - } - ) - _next_request_params["api-version"] = self._config.api_version - _request = HttpRequest( - "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params - ) - path_format_arguments = { - "Endpoint": self._serialize.url( - "self._config.endpoint", self._config.endpoint, "str", skip_quote=True - ), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - return _request - - async def extract_data(pipeline_response): - deserialized = pipeline_response.http_response.json() - list_of_elem = deserialized["value"] - if cls: - list_of_elem = cls(list_of_elem) # type: ignore - return deserialized.get("nextLink") or None, AsyncList(list_of_elem) - - async def get_next(next_link=None): - _request = prepare_request(next_link) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - return pipeline_response - - polling: Union[bool, AsyncPollingMethod] = kwargs.pop("polling", True) - lro_delay = kwargs.pop("polling_interval", self._config.polling_interval) - cont_token: Optional[str] = kwargs.pop("continuation_token", None) - if cont_token is None: - raw_result = await self._update_qnas_initial( - project_name=project_name, - qnas=qnas, - content_type=content_type, - cls=lambda x, y, z: x, - headers=_headers, - params=_params, - **kwargs - ) - kwargs.pop("error_map", None) - - def get_long_running_output(pipeline_response): - async def internal_get_next(next_link=None): - if next_link is None: - return pipeline_response - return await get_next(next_link) - - return AsyncItemPaged(internal_get_next, extract_data) - - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - - if polling is True: - polling_method: AsyncPollingMethod = cast( - AsyncPollingMethod, - AsyncLROBasePolling(lro_delay, path_format_arguments=path_format_arguments, **kwargs), - ) - elif polling is False: - polling_method = cast(AsyncPollingMethod, AsyncNoPolling()) - else: - polling_method = polling - if cont_token: - return AsyncLROPoller[AsyncIterable[JSON]].from_continuation_token( - polling_method=polling_method, - continuation_token=cont_token, - client=self._client, - deserialization_callback=get_long_running_output, - ) - return AsyncLROPoller[AsyncIterable[JSON]]( - self._client, raw_result, get_long_running_output, polling_method # type: ignore - ) - - @overload - async def add_feedback( # pylint: disable=inconsistent-return-statements - self, project_name: str, feedback: JSON, *, content_type: str = "application/json", **kwargs: Any - ) -> None: - """Update Active Learning feedback. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/add-feedback - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param feedback: Feedback for Active Learning. Required. - :type feedback: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - feedback = { - "records": [ - { - "qnaId": 0, # Optional. Unique ID of the QnA. - "userId": "str", # Optional. Unique identifier of the user. - "userQuestion": "str" # Optional. User suggested question - for the QnA. - } - ] - } - """ - - @overload - async def add_feedback( # pylint: disable=inconsistent-return-statements - self, project_name: str, feedback: IO[bytes], *, content_type: str = "application/json", **kwargs: Any - ) -> None: - """Update Active Learning feedback. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/add-feedback - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param feedback: Feedback for Active Learning. Required. - :type feedback: IO[bytes] - :keyword content_type: Body Parameter content-type. Content type parameter for binary body. - Default value is "application/json". - :paramtype content_type: str - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - """ - - @distributed_trace_async - async def add_feedback( # pylint: disable=inconsistent-return-statements - self, project_name: str, feedback: Union[JSON, IO[bytes]], **kwargs: Any - ) -> None: - """Update Active Learning feedback. - - See - https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/question-answering-projects/add-feedback - for more information. - - :param project_name: The name of the project to use. Required. - :type project_name: str - :param feedback: Feedback for Active Learning. Is either a JSON type or a IO[bytes] type. - Required. - :type feedback: JSON or IO[bytes] - :return: None - :rtype: None - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # JSON input template you can fill out and use as your body input. - feedback = { - "records": [ - { - "qnaId": 0, # Optional. Unique ID of the QnA. - "userId": "str", # Optional. Unique identifier of the user. - "userQuestion": "str" # Optional. User suggested question - for the QnA. - } - ] - } - """ - error_map = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) - _params = kwargs.pop("params", {}) or {} - - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[None] = kwargs.pop("cls", None) - - content_type = content_type or "application/json" - _json = None - _content = None - if isinstance(feedback, (IOBase, bytes)): - _content = feedback - else: - _json = feedback - - _request = build_authoring_add_feedback_request( - project_name=project_name, - content_type=content_type, - api_version=self._config.api_version, - json=_json, - content=_content, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [204]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if cls: - return cls(pipeline_response, None, {}) # type: ignore diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_operations/_patch.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_operations/_patch.py deleted file mode 100644 index f7dd32510333..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_operations/_patch.py +++ /dev/null @@ -1,20 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -"""Customize generated code here. - -Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize -""" -from typing import List - -__all__: List[str] = [] # Add all objects you want publicly available to users at this package level - - -def patch_sdk(): - """Do not remove from this file. - - `patch_sdk` is a last resort escape hatch that allows you to do customizations - you can't accomplish using the techniques described in - https://aka.ms/azsdk/python/dpcodegen/python/customize - """ diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_patch.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_patch.py deleted file mode 100644 index d094331741aa..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_patch.py +++ /dev/null @@ -1,82 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -"""Customize generated code here. - -Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize -""" -from typing import List, Union, Any -from azure.core.credentials import AzureKeyCredential -from azure.core.credentials_async import AsyncTokenCredential -from azure.core.pipeline.policies import AzureKeyCredentialPolicy, AsyncBearerTokenCredentialPolicy -from ._client import AuthoringClient as AuthoringClientGenerated -from .._patch import POLLING_INTERVAL_DEFAULT - - -def _authentication_policy(credential, **kwargs): - if credential is None: - raise ValueError("Parameter 'credential' must not be None.") - if isinstance(credential, AzureKeyCredential): - authentication_policy = AzureKeyCredentialPolicy( - name="Ocp-Apim-Subscription-Key", credential=credential, **kwargs - ) - elif hasattr(credential, "get_token"): - authentication_policy = AsyncBearerTokenCredentialPolicy( - credential, *kwargs.pop("credential_scopes", ["https://cognitiveservices.azure.com/.default"]), **kwargs - ) - else: - raise TypeError( - "Unsupported credential: {}. Use an instance of AzureKeyCredential " - "or a token credential from azure.identity".format(type(credential)) - ) - return authentication_policy - - -class AuthoringClient(AuthoringClientGenerated): - """The language service API is a suite of natural language processing (NLP) skills built with - best-in-class Microsoft machine learning algorithms. The API can be used to analyze - unstructured text for tasks such as sentiment analysis, key phrase extraction, language - detection and question answering. Further documentation can be found in - https://learn.microsoft.com/azure/cognitive-services/language-service/overview - - :param endpoint: Supported Cognitive Services endpoint (e.g., - https://:code:``.cognitiveservices.azure.com). - :type endpoint: str - :param credential: Credential needed for the client to connect to Azure. - This can be the an instance of AzureKeyCredential if using a Language API key - or a token credential from :mod:`azure.identity`. - :type credential: ~azure.core.credentials.AzureKeyCredential or ~azure.core.credentials_async.AsyncTokenCredential - :keyword api_version: Api Version. The default value is "2021-10-01". Note that overriding this - default value may result in unsupported behavior. - :paramtype api_version: str - :keyword int polling_interval: Default waiting time between two polls for LRO operations if no - Retry-After header is present. - """ - - def __init__( - self, endpoint: str, credential: Union[AzureKeyCredential, AsyncTokenCredential], **kwargs: Any - ) -> None: - try: - endpoint = endpoint.rstrip("/") - except AttributeError as exc: - raise ValueError("Parameter 'endpoint' must be a string.") from exc - super().__init__( - endpoint=endpoint, - credential=credential, # type: ignore - authentication_policy=kwargs.pop("authentication_policy", _authentication_policy(credential, **kwargs)), - polling_interval=kwargs.pop("polling_interval", POLLING_INTERVAL_DEFAULT), - **kwargs - ) - - -__all__: List[str] = ["AuthoringClient"] # Add all objects you want publicly available to users at this package level - - -def patch_sdk(): - """Do not remove from this file. - - `patch_sdk` is a last resort escape hatch that allows you to do customizations - you can't accomplish using the techniques described in - https://aka.ms/azsdk/python/dpcodegen/python/customize - """ diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_vendor.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_vendor.py deleted file mode 100644 index 5c352a49bf55..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/aio/_vendor.py +++ /dev/null @@ -1,26 +0,0 @@ -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from abc import ABC -from typing import TYPE_CHECKING - -from ._configuration import AuthoringClientConfiguration - -if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports - from azure.core import AsyncPipelineClient - - from .._serialization import Deserializer, Serializer - - -class AuthoringClientMixinABC(ABC): - """DO NOT use this class. It is for internal typing use only.""" - - _client: "AsyncPipelineClient" - _config: AuthoringClientConfiguration - _serialize: "Serializer" - _deserialize: "Deserializer" diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/py.typed b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/py.typed deleted file mode 100644 index e5aff4f83af8..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/authoring/py.typed +++ /dev/null @@ -1 +0,0 @@ -# Marker file for PEP 561. \ No newline at end of file diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/__init__.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/__init__.py index 7e8a2f45b6f4..58f0fc8924ff 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/__init__.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/__init__.py @@ -2,31 +2,53 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position -from ._models import AnswerSpan -from ._patch import AnswersFromTextOptions -from ._models import AnswersFromTextResult -from ._models import AnswersOptions -from ._models import AnswersResult -from ._models import Error -from ._models import ErrorResponse -from ._models import InnerErrorModel -from ._models import KnowledgeBaseAnswer -from ._models import KnowledgeBaseAnswerContext -from ._models import KnowledgeBaseAnswerDialog -from ._models import KnowledgeBaseAnswerPrompt -from ._patch import MetadataFilter -from ._models import QueryFilters -from ._models import ShortAnswerOptions -from ._models import TextAnswer -from ._models import TextDocument +from typing import TYPE_CHECKING -from ._enums import ErrorCode -from ._enums import InnerErrorCode +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + +from ._models import ( # type: ignore + AnswerSpan, + AnswersFromTextOptions, + AnswersFromTextResult, + AnswersOptions, + AnswersResult, + Error, + ErrorResponse, + InnerErrorModel, + KnowledgeBaseAnswer, + KnowledgeBaseAnswerContext, + KnowledgeBaseAnswerDialog, + KnowledgeBaseAnswerPrompt, + MatchingPolicy, + MetadataFilter, + MetadataRecord, + PrebuiltQueryMatchingPolicy, + QueryFilters, + QueryPreferences, + ShortAnswerOptions, + TextAnswer, + TextDocument, +) + +from ._enums import ( # type: ignore + ErrorCode, + InnerErrorCode, + LogicalOperationKind, + MatchingPolicyFieldsType, + MatchingPolicyKind, + RankerKind, + Scorer, + StringIndexType, +) +from ._patch import __all__ as _patch_all +from ._patch import * from ._patch import patch_sdk as _patch_sdk __all__ = [ @@ -42,13 +64,23 @@ "KnowledgeBaseAnswerContext", "KnowledgeBaseAnswerDialog", "KnowledgeBaseAnswerPrompt", + "MatchingPolicy", "MetadataFilter", + "MetadataRecord", + "PrebuiltQueryMatchingPolicy", "QueryFilters", + "QueryPreferences", "ShortAnswerOptions", "TextAnswer", "TextDocument", "ErrorCode", "InnerErrorCode", + "LogicalOperationKind", + "MatchingPolicyFieldsType", + "MatchingPolicyKind", + "RankerKind", + "Scorer", + "StringIndexType", ] - +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore _patch_sdk() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_enums.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_enums.py index dbc6c082e762..bb8e51997160 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_enums.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_enums.py @@ -2,7 +2,7 @@ # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- @@ -14,27 +14,139 @@ class ErrorCode(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Human-readable error code.""" INVALID_REQUEST = "InvalidRequest" + """Invalid request error""" INVALID_ARGUMENT = "InvalidArgument" + """Invalid argument error""" UNAUTHORIZED = "Unauthorized" + """Unauthorized access error""" FORBIDDEN = "Forbidden" + """Forbidden access error""" NOT_FOUND = "NotFound" + """Not found error""" PROJECT_NOT_FOUND = "ProjectNotFound" + """Project not found error""" OPERATION_NOT_FOUND = "OperationNotFound" + """Operation not found error""" AZURE_COGNITIVE_SEARCH_NOT_FOUND = "AzureCognitiveSearchNotFound" + """Azure Cognitive Search not found error""" AZURE_COGNITIVE_SEARCH_INDEX_NOT_FOUND = "AzureCognitiveSearchIndexNotFound" + """Azure Cognitive Search index not found error""" TOO_MANY_REQUESTS = "TooManyRequests" + """Too many requests error""" AZURE_COGNITIVE_SEARCH_THROTTLING = "AzureCognitiveSearchThrottling" + """Azure Cognitive Search throttling error""" AZURE_COGNITIVE_SEARCH_INDEX_LIMIT_REACHED = "AzureCognitiveSearchIndexLimitReached" + """Azure Cognitive Search index limit reached error""" INTERNAL_SERVER_ERROR = "InternalServerError" + """Internal server error""" SERVICE_UNAVAILABLE = "ServiceUnavailable" + """Service unavailable error""" + TIMEOUT = "Timeout" + """Timeout error""" + QUOTA_EXCEEDED = "QuotaExceeded" + """Quota exceeded error""" + CONFLICT = "Conflict" + """Conflict error""" + WARNING = "Warning" + """Warning error""" class InnerErrorCode(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Human-readable error code.""" INVALID_REQUEST = "InvalidRequest" + """Invalid request error""" INVALID_PARAMETER_VALUE = "InvalidParameterValue" + """Invalid parameter value error""" KNOWLEDGE_BASE_NOT_FOUND = "KnowledgeBaseNotFound" + """Knowledge base not found error""" AZURE_COGNITIVE_SEARCH_NOT_FOUND = "AzureCognitiveSearchNotFound" + """Azure Cognitive Search not found error""" AZURE_COGNITIVE_SEARCH_THROTTLING = "AzureCognitiveSearchThrottling" + """Azure Cognitive Search throttling error""" EXTRACTION_FAILURE = "ExtractionFailure" + """Extraction failure error""" + INVALID_REQUEST_BODY_FORMAT = "InvalidRequestBodyFormat" + """Invalid request body format error""" + EMPTY_REQUEST = "EmptyRequest" + """Empty request error""" + MISSING_INPUT_DOCUMENTS = "MissingInputDocuments" + """Missing input documents error""" + INVALID_DOCUMENT = "InvalidDocument" + """Invalid document error""" + MODEL_VERSION_INCORRECT = "ModelVersionIncorrect" + """Model version incorrect error""" + INVALID_DOCUMENT_BATCH = "InvalidDocumentBatch" + """Invalid document batch error""" + UNSUPPORTED_LANGUAGE_CODE = "UnsupportedLanguageCode" + """Unsupported language code error""" + INVALID_COUNTRY_HINT = "InvalidCountryHint" + """Invalid country hint error""" + + +class LogicalOperationKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Set to 'OR' or 'AND' for using corresponding logical operation.""" + + AND = "AND" + """'AND' for using corresponding logical operation.""" + OR = "OR" + """Set 'OR' for using corresponding logical operation.""" + + +class MatchingPolicyFieldsType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Fields to be considred for matching policy.""" + + QUESTIONS = "Questions" + """Include 'Questions' field""" + ANSWER = "Answer" + """Include 'Answer' field""" + + +class MatchingPolicyKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Kind of matching policy to be used.""" + + PREBUILT = "Prebuilt" + """Prebuilt weights will be used for giving preference to question and answer columns when quering + AI search""" + + +class RankerKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of ranker to be used.""" + + QUESTION_ONLY = "QuestionOnly" + """Question only ranker.""" + DEFAULT = "Default" + """Default ranker.""" + + +class Scorer(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of scorer to be used.""" + + CLASSIC = "Classic" + """Set this value for scoring based on classic algorithms like wordnet, tfidf, ngram etc.""" + TRANSFORMER = "Transformer" + """Set this value for scoring based on transformer based models.""" + SEMANTIC = "Semantic" + """Set this scorer to use AI search semantic based ranking. Semantic ranking should be configured + for this to be used.""" + + +class StringIndexType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Specifies the method used to interpret string offsets. Defaults to Text + Elements (Graphemes) according to Unicode v8.0.0. For additional information + see `https://aka.ms/text-analytics-offsets `_. + """ + + TEXT_ELEMENTS_V8 = "TextElements_v8" + """Returned offset and length values will correspond to TextElements (Graphemes + and Grapheme clusters) confirming to the Unicode 8.0.0 standard. Use this + option if your application is written in .Net Framework or .Net Core and you + will be using StringInfo.""" + UNICODE_CODE_POINT = "UnicodeCodePoint" + """Returned offset and length values will correspond to Unicode code points. Use + this option if your application is written in a language that support Unicode, + for example Python.""" + UTF16_CODE_UNIT = "Utf16CodeUnit" + """Returned offset and length values will correspond to UTF-16 code units. Use + this option if your application is written in a language that support Unicode, + for example Java, JavaScript.""" diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_models.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_models.py index 54bf9f77f828..5e8fa7b4eccd 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_models.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_models.py @@ -1,99 +1,111 @@ +# pylint: disable=line-too-long,useless-suppression # coding=utf-8 -# pylint: disable=too-many-lines # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) AutoRest Code Generator. +# Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +# pylint: disable=useless-super-delegation -import sys -from typing import Any, Dict, List, Optional, TYPE_CHECKING, Union +from typing import Any, Literal, Mapping, Optional, TYPE_CHECKING, Union, overload -from .. import _serialization - -if sys.version_info >= (3, 9): - from collections.abc import MutableMapping -else: - from typing import MutableMapping # type: ignore # pylint: disable=ungrouped-imports +from .._utils.model_base import Model as _Model, rest_discriminator, rest_field +from ._enums import MatchingPolicyKind if TYPE_CHECKING: - # pylint: disable=unused-import,ungrouped-imports from .. import models as _models -JSON = MutableMapping[str, Any] # pylint: disable=unsubscriptable-object -class AnswersFromTextOptions(_serialization.Model): +class AnswersFromTextOptions(_Model): """The question and text record parameters to answer. - All required parameters must be populated in order to send to server. - :ivar question: User question to query against the given text records. Required. :vartype question: str :ivar text_documents: Text records to be searched for given question. Required. :vartype text_documents: list[~azure.ai.language.questionanswering.models.TextDocument] :ivar language: Language of the text records. This is BCP-47 representation of a language. For - example, use "en" for English; "es" for Spanish etc. If not set, use "en" for English as - default. + example, use "en" for English; "es" for Spanish etc. If not set, use "en" for + English as default. :vartype language: str + :ivar string_index_type: Specifies the method used to interpret string offsets. Defaults to + Text + Elements (Graphemes) according to Unicode v8.0.0. For additional information + see `https://aka.ms/text-analytics-offsets `_. Known + values are: "TextElements_v8", "UnicodeCodePoint", and "Utf16CodeUnit". + :vartype string_index_type: str or ~azure.ai.language.questionanswering.models.StringIndexType """ - _validation = { - "question": {"required": True}, - "text_documents": {"required": True}, - } - - _attribute_map = { - "question": {"key": "question", "type": "str"}, - "text_documents": {"key": "records", "type": "[TextDocument]"}, - "language": {"key": "language", "type": "str"}, - } - + question: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """User question to query against the given text records. Required.""" + text_documents: list["_models.TextDocument"] = rest_field( + name="records", visibility=["read", "create", "update", "delete", "query"] + ) + """Text records to be searched for given question. Required.""" + language: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Language of the text records. This is BCP-47 representation of a language. For + example, use \"en\" for English; \"es\" for Spanish etc. If not set, use \"en\" for + English as default.""" + string_index_type: Optional[Union[str, "_models.StringIndexType"]] = rest_field( + name="stringIndexType", visibility=["read", "create", "update", "delete", "query"] + ) + """Specifies the method used to interpret string offsets. Defaults to Text + Elements (Graphemes) according to Unicode v8.0.0. For additional information + see `https://aka.ms/text-analytics-offsets `_. Known + values are: \"TextElements_v8\", \"UnicodeCodePoint\", and \"Utf16CodeUnit\".""" + + @overload def __init__( self, *, question: str, - text_documents: List["_models.TextDocument"], + text_documents: list["_models.TextDocument"], language: Optional[str] = None, - **kwargs: Any - ) -> None: + string_index_type: Optional[Union[str, "_models.StringIndexType"]] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: """ - :keyword question: User question to query against the given text records. Required. - :paramtype question: str - :keyword text_documents: Text records to be searched for given question. Required. - :paramtype text_documents: list[~azure.ai.language.questionanswering.models.TextDocument] - :keyword language: Language of the text records. This is BCP-47 representation of a language. - For example, use "en" for English; "es" for Spanish etc. If not set, use "en" for English as - default. - :paramtype language: str + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] """ - super().__init__(**kwargs) - self.question = question - self.text_documents = text_documents - self.language = language + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) -class AnswersFromTextResult(_serialization.Model): + +class AnswersFromTextResult(_Model): """Represents the answer results. :ivar answers: Represents the answer results. :vartype answers: list[~azure.ai.language.questionanswering.models.TextAnswer] """ - _attribute_map = { - "answers": {"key": "answers", "type": "[TextAnswer]"}, - } + answers: Optional[list["_models.TextAnswer"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Represents the answer results.""" + + @overload + def __init__( + self, + *, + answers: Optional[list["_models.TextAnswer"]] = None, + ) -> None: ... - def __init__(self, *, answers: Optional[List["_models.TextAnswer"]] = None, **kwargs: Any) -> None: + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: """ - :keyword answers: Represents the answer results. - :paramtype answers: list[~azure.ai.language.questionanswering.models.TextAnswer] + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] """ - super().__init__(**kwargs) - self.answers = answers + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) -class AnswersOptions(_serialization.Model): + +class AnswersOptions(_Model): """Parameters to query a knowledge base. :ivar qna_id: Exact QnA ID to fetch from the knowledge base, this field takes priority over @@ -109,33 +121,55 @@ class AnswersOptions(_serialization.Model): :vartype confidence_threshold: float :ivar answer_context: Context object with previous QnA's information. :vartype answer_context: ~azure.ai.language.questionanswering.models.KnowledgeBaseAnswerContext - :ivar ranker_kind: Type of ranker to be used. - :vartype ranker_kind: str + :ivar ranker_kind: Type of ranker to be used. Known values are: "QuestionOnly" and "Default". + :vartype ranker_kind: str or ~azure.ai.language.questionanswering.models.RankerKind :ivar filters: Filter QnAs based on given metadata list and knowledge base sources. :vartype filters: ~azure.ai.language.questionanswering.models.QueryFilters :ivar short_answer_options: To configure Answer span prediction feature. :vartype short_answer_options: ~azure.ai.language.questionanswering.models.ShortAnswerOptions :ivar include_unstructured_sources: (Optional) Flag to enable Query over Unstructured Sources. :vartype include_unstructured_sources: bool + :ivar query_preferences: To fine tune query results. + :vartype query_preferences: ~azure.ai.language.questionanswering.models.QueryPreferences """ - _validation = { - "confidence_threshold": {"maximum": 1, "minimum": 0}, - } - - _attribute_map = { - "qna_id": {"key": "qnaId", "type": "int"}, - "question": {"key": "question", "type": "str"}, - "top": {"key": "top", "type": "int"}, - "user_id": {"key": "userId", "type": "str"}, - "confidence_threshold": {"key": "confidenceScoreThreshold", "type": "float"}, - "answer_context": {"key": "context", "type": "KnowledgeBaseAnswerContext"}, - "ranker_kind": {"key": "rankerType", "type": "str"}, - "filters": {"key": "filters", "type": "QueryFilters"}, - "short_answer_options": {"key": "answerSpanRequest", "type": "ShortAnswerOptions"}, - "include_unstructured_sources": {"key": "includeUnstructuredSources", "type": "bool"}, - } - + qna_id: Optional[int] = rest_field(name="qnaId", visibility=["read", "create", "update", "delete", "query"]) + """Exact QnA ID to fetch from the knowledge base, this field takes priority over + question.""" + question: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """User question to query against the knowledge base.""" + top: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Max number of answers to be returned for the question.""" + user_id: Optional[str] = rest_field(name="userId", visibility=["read", "create", "update", "delete", "query"]) + """Unique identifier for the user.""" + confidence_threshold: Optional[float] = rest_field( + name="confidenceScoreThreshold", visibility=["read", "create", "update", "delete", "query"] + ) + """Minimum threshold score for answers, value ranges from 0 to 1.""" + answer_context: Optional["_models.KnowledgeBaseAnswerContext"] = rest_field( + name="context", visibility=["read", "create", "update", "delete", "query"] + ) + """Context object with previous QnA's information.""" + ranker_kind: Optional[Union[str, "_models.RankerKind"]] = rest_field( + name="rankerType", visibility=["read", "create", "update", "delete", "query"] + ) + """Type of ranker to be used. Known values are: \"QuestionOnly\" and \"Default\".""" + filters: Optional["_models.QueryFilters"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Filter QnAs based on given metadata list and knowledge base sources.""" + short_answer_options: Optional["_models.ShortAnswerOptions"] = rest_field( + name="answerSpanRequest", visibility=["read", "create", "update", "delete", "query"] + ) + """To configure Answer span prediction feature.""" + include_unstructured_sources: Optional[bool] = rest_field( + name="includeUnstructuredSources", visibility=["read", "create", "update", "delete", "query"] + ) + """(Optional) Flag to enable Query over Unstructured Sources.""" + query_preferences: Optional["_models.QueryPreferences"] = rest_field( + name="queryPreferences", visibility=["read", "create", "update", "delete", "query"] + ) + """To fine tune query results.""" + + @overload def __init__( self, *, @@ -145,51 +179,25 @@ def __init__( user_id: Optional[str] = None, confidence_threshold: Optional[float] = None, answer_context: Optional["_models.KnowledgeBaseAnswerContext"] = None, - ranker_kind: Optional[str] = None, + ranker_kind: Optional[Union[str, "_models.RankerKind"]] = None, filters: Optional["_models.QueryFilters"] = None, short_answer_options: Optional["_models.ShortAnswerOptions"] = None, include_unstructured_sources: Optional[bool] = None, - **kwargs: Any - ) -> None: - """ - :keyword qna_id: Exact QnA ID to fetch from the knowledge base, this field takes priority over - question. - :paramtype qna_id: int - :keyword question: User question to query against the knowledge base. - :paramtype question: str - :keyword top: Max number of answers to be returned for the question. - :paramtype top: int - :keyword user_id: Unique identifier for the user. - :paramtype user_id: str - :keyword confidence_threshold: Minimum threshold score for answers, value ranges from 0 to 1. - :paramtype confidence_threshold: float - :keyword answer_context: Context object with previous QnA's information. - :paramtype answer_context: - ~azure.ai.language.questionanswering.models.KnowledgeBaseAnswerContext - :keyword ranker_kind: Type of ranker to be used. - :paramtype ranker_kind: str - :keyword filters: Filter QnAs based on given metadata list and knowledge base sources. - :paramtype filters: ~azure.ai.language.questionanswering.models.QueryFilters - :keyword short_answer_options: To configure Answer span prediction feature. - :paramtype short_answer_options: ~azure.ai.language.questionanswering.models.ShortAnswerOptions - :keyword include_unstructured_sources: (Optional) Flag to enable Query over Unstructured - Sources. - :paramtype include_unstructured_sources: bool - """ - super().__init__(**kwargs) - self.qna_id = qna_id - self.question = question - self.top = top - self.user_id = user_id - self.confidence_threshold = confidence_threshold - self.answer_context = answer_context - self.ranker_kind = ranker_kind - self.filters = filters - self.short_answer_options = short_answer_options - self.include_unstructured_sources = include_unstructured_sources - - -class AnswerSpan(_serialization.Model): + query_preferences: Optional["_models.QueryPreferences"] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class AnswerSpan(_Model): """Answer span object of QnA. :ivar text: Predicted text of answer span. @@ -202,17 +210,18 @@ class AnswerSpan(_serialization.Model): :vartype length: int """ - _validation = { - "confidence": {"maximum": 1, "minimum": 0}, - } - - _attribute_map = { - "text": {"key": "text", "type": "str"}, - "confidence": {"key": "confidenceScore", "type": "float"}, - "offset": {"key": "offset", "type": "int"}, - "length": {"key": "length", "type": "int"}, - } - + text: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Predicted text of answer span.""" + confidence: Optional[float] = rest_field( + name="confidenceScore", visibility=["read", "create", "update", "delete", "query"] + ) + """Predicted score of answer span, value ranges from 0 to 1.""" + offset: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The answer span offset from the start of answer.""" + length: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The length of the answer span.""" + + @overload def __init__( self, *, @@ -220,55 +229,59 @@ def __init__( confidence: Optional[float] = None, offset: Optional[int] = None, length: Optional[int] = None, - **kwargs: Any - ) -> None: - """ - :keyword text: Predicted text of answer span. - :paramtype text: str - :keyword confidence: Predicted score of answer span, value ranges from 0 to 1. - :paramtype confidence: float - :keyword offset: The answer span offset from the start of answer. - :paramtype offset: int - :keyword length: The length of the answer span. - :paramtype length: int - """ - super().__init__(**kwargs) - self.text = text - self.confidence = confidence - self.offset = offset - self.length = length - - -class AnswersResult(_serialization.Model): + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class AnswersResult(_Model): """Represents List of Question Answers. :ivar answers: Represents Answer Result list. :vartype answers: list[~azure.ai.language.questionanswering.models.KnowledgeBaseAnswer] """ - _attribute_map = { - "answers": {"key": "answers", "type": "[KnowledgeBaseAnswer]"}, - } + answers: Optional[list["_models.KnowledgeBaseAnswer"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Represents Answer Result list.""" + + @overload + def __init__( + self, + *, + answers: Optional[list["_models.KnowledgeBaseAnswer"]] = None, + ) -> None: ... - def __init__(self, *, answers: Optional[List["_models.KnowledgeBaseAnswer"]] = None, **kwargs: Any) -> None: + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: """ - :keyword answers: Represents Answer Result list. - :paramtype answers: list[~azure.ai.language.questionanswering.models.KnowledgeBaseAnswer] + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] """ - super().__init__(**kwargs) - self.answers = answers + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) -class Error(_serialization.Model): - """The error object. - All required parameters must be populated in order to send to server. +class Error(_Model): + """The error response object returned when the service encounters some errors during processing + the request. :ivar code: One of a server-defined set of error codes. Required. Known values are: "InvalidRequest", "InvalidArgument", "Unauthorized", "Forbidden", "NotFound", "ProjectNotFound", "OperationNotFound", "AzureCognitiveSearchNotFound", "AzureCognitiveSearchIndexNotFound", "TooManyRequests", "AzureCognitiveSearchThrottling", - "AzureCognitiveSearchIndexLimitReached", "InternalServerError", and "ServiceUnavailable". + "AzureCognitiveSearchIndexLimitReached", "InternalServerError", "ServiceUnavailable", + "Timeout", "QuotaExceeded", "Conflict", and "Warning". :vartype code: str or ~azure.ai.language.questionanswering.models.ErrorCode :ivar message: A human-readable representation of the error. Required. :vartype message: str @@ -281,84 +294,86 @@ class Error(_serialization.Model): :vartype innererror: ~azure.ai.language.questionanswering.models.InnerErrorModel """ - _validation = { - "code": {"required": True}, - "message": {"required": True}, - } - - _attribute_map = { - "code": {"key": "code", "type": "str"}, - "message": {"key": "message", "type": "str"}, - "target": {"key": "target", "type": "str"}, - "details": {"key": "details", "type": "[Error]"}, - "innererror": {"key": "innererror", "type": "InnerErrorModel"}, - } - + code: Union[str, "_models.ErrorCode"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """One of a server-defined set of error codes. Required. Known values are: \"InvalidRequest\", + \"InvalidArgument\", \"Unauthorized\", \"Forbidden\", \"NotFound\", \"ProjectNotFound\", + \"OperationNotFound\", \"AzureCognitiveSearchNotFound\", \"AzureCognitiveSearchIndexNotFound\", + \"TooManyRequests\", \"AzureCognitiveSearchThrottling\", + \"AzureCognitiveSearchIndexLimitReached\", \"InternalServerError\", \"ServiceUnavailable\", + \"Timeout\", \"QuotaExceeded\", \"Conflict\", and \"Warning\".""" + message: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A human-readable representation of the error. Required.""" + target: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The target of the error.""" + details: Optional[list["_models.Error"]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """An array of details about specific errors that led to this reported error.""" + innererror: Optional["_models.InnerErrorModel"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """An object containing more specific information than the current object about the error.""" + + @overload def __init__( self, *, code: Union[str, "_models.ErrorCode"], message: str, target: Optional[str] = None, - details: Optional[List["_models.Error"]] = None, + details: Optional[list["_models.Error"]] = None, innererror: Optional["_models.InnerErrorModel"] = None, - **kwargs: Any - ) -> None: - """ - :keyword code: One of a server-defined set of error codes. Required. Known values are: - "InvalidRequest", "InvalidArgument", "Unauthorized", "Forbidden", "NotFound", - "ProjectNotFound", "OperationNotFound", "AzureCognitiveSearchNotFound", - "AzureCognitiveSearchIndexNotFound", "TooManyRequests", "AzureCognitiveSearchThrottling", - "AzureCognitiveSearchIndexLimitReached", "InternalServerError", and "ServiceUnavailable". - :paramtype code: str or ~azure.ai.language.questionanswering.models.ErrorCode - :keyword message: A human-readable representation of the error. Required. - :paramtype message: str - :keyword target: The target of the error. - :paramtype target: str - :keyword details: An array of details about specific errors that led to this reported error. - :paramtype details: list[~azure.ai.language.questionanswering.models.Error] - :keyword innererror: An object containing more specific information than the current object - about the error. - :paramtype innererror: ~azure.ai.language.questionanswering.models.InnerErrorModel - """ - super().__init__(**kwargs) - self.code = code - self.message = message - self.target = target - self.details = details - self.innererror = innererror - - -class ErrorResponse(_serialization.Model): + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class ErrorResponse(_Model): """Error response. - :ivar error: The error object. + :ivar error: The error object. Required. :vartype error: ~azure.ai.language.questionanswering.models.Error """ - _attribute_map = { - "error": {"key": "error", "type": "Error"}, - } + error: "_models.Error" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The error object. Required.""" - def __init__(self, *, error: Optional["_models.Error"] = None, **kwargs: Any) -> None: + @overload + def __init__( + self, + *, + error: "_models.Error", + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: """ - :keyword error: The error object. - :paramtype error: ~azure.ai.language.questionanswering.models.Error + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] """ - super().__init__(**kwargs) - self.error = error + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) -class InnerErrorModel(_serialization.Model): - """An object containing more specific information about the error. As per Microsoft One API - guidelines - - https://github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses. - All required parameters must be populated in order to send to server. +class InnerErrorModel(_Model): + """An object containing more specific information about the error. As per + Microsoft One API guidelines - + `https://github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses + `_. :ivar code: One of a server-defined set of error codes. Required. Known values are: "InvalidRequest", "InvalidParameterValue", "KnowledgeBaseNotFound", - "AzureCognitiveSearchNotFound", "AzureCognitiveSearchThrottling", and "ExtractionFailure". + "AzureCognitiveSearchNotFound", "AzureCognitiveSearchThrottling", "ExtractionFailure", + "InvalidRequestBodyFormat", "EmptyRequest", "MissingInputDocuments", "InvalidDocument", + "ModelVersionIncorrect", "InvalidDocumentBatch", "UnsupportedLanguageCode", and + "InvalidCountryHint". :vartype code: str or ~azure.ai.language.questionanswering.models.InnerErrorCode :ivar message: Error message. Required. :vartype message: str @@ -371,53 +386,47 @@ class InnerErrorModel(_serialization.Model): :vartype innererror: ~azure.ai.language.questionanswering.models.InnerErrorModel """ - _validation = { - "code": {"required": True}, - "message": {"required": True}, - } - - _attribute_map = { - "code": {"key": "code", "type": "str"}, - "message": {"key": "message", "type": "str"}, - "details": {"key": "details", "type": "{str}"}, - "target": {"key": "target", "type": "str"}, - "innererror": {"key": "innererror", "type": "InnerErrorModel"}, - } - + code: Union[str, "_models.InnerErrorCode"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """One of a server-defined set of error codes. Required. Known values are: \"InvalidRequest\", + \"InvalidParameterValue\", \"KnowledgeBaseNotFound\", \"AzureCognitiveSearchNotFound\", + \"AzureCognitiveSearchThrottling\", \"ExtractionFailure\", \"InvalidRequestBodyFormat\", + \"EmptyRequest\", \"MissingInputDocuments\", \"InvalidDocument\", \"ModelVersionIncorrect\", + \"InvalidDocumentBatch\", \"UnsupportedLanguageCode\", and \"InvalidCountryHint\".""" + message: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Error message. Required.""" + details: Optional[dict[str, str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Error details.""" + target: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Error target.""" + innererror: Optional["_models.InnerErrorModel"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """An object containing more specific information than the current object about + the error.""" + + @overload def __init__( self, *, code: Union[str, "_models.InnerErrorCode"], message: str, - details: Optional[Dict[str, str]] = None, + details: Optional[dict[str, str]] = None, target: Optional[str] = None, innererror: Optional["_models.InnerErrorModel"] = None, - **kwargs: Any - ) -> None: - """ - :keyword code: One of a server-defined set of error codes. Required. Known values are: - "InvalidRequest", "InvalidParameterValue", "KnowledgeBaseNotFound", - "AzureCognitiveSearchNotFound", "AzureCognitiveSearchThrottling", and "ExtractionFailure". - :paramtype code: str or ~azure.ai.language.questionanswering.models.InnerErrorCode - :keyword message: Error message. Required. - :paramtype message: str - :keyword details: Error details. - :paramtype details: dict[str, str] - :keyword target: Error target. - :paramtype target: str - :keyword innererror: An object containing more specific information than the current object - about the error. - :paramtype innererror: ~azure.ai.language.questionanswering.models.InnerErrorModel - """ - super().__init__(**kwargs) - self.code = code - self.message = message - self.details = details - self.target = target - self.innererror = innererror - - -class KnowledgeBaseAnswer(_serialization.Model): + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class KnowledgeBaseAnswer(_Model): """Represents knowledge base answer. :ivar questions: List of questions associated with the answer. @@ -439,68 +448,57 @@ class KnowledgeBaseAnswer(_serialization.Model): :vartype short_answer: ~azure.ai.language.questionanswering.models.AnswerSpan """ - _validation = { - "confidence": {"maximum": 1, "minimum": 0}, - } - - _attribute_map = { - "questions": {"key": "questions", "type": "[str]"}, - "answer": {"key": "answer", "type": "str"}, - "confidence": {"key": "confidenceScore", "type": "float"}, - "qna_id": {"key": "id", "type": "int"}, - "source": {"key": "source", "type": "str"}, - "metadata": {"key": "metadata", "type": "{str}"}, - "dialog": {"key": "dialog", "type": "KnowledgeBaseAnswerDialog"}, - "short_answer": {"key": "answerSpan", "type": "AnswerSpan"}, - } - + questions: Optional[list[str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """List of questions associated with the answer.""" + answer: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Answer text.""" + confidence: Optional[float] = rest_field( + name="confidenceScore", visibility=["read", "create", "update", "delete", "query"] + ) + """Answer confidence score, value ranges from 0 to 1.""" + qna_id: Optional[int] = rest_field(name="id", visibility=["read", "create", "update", "delete", "query"]) + """ID of the QnA result.""" + source: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Source of QnA result.""" + metadata: Optional[dict[str, str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Metadata associated with the answer, useful to categorize or filter question + answers.""" + dialog: Optional["_models.KnowledgeBaseAnswerDialog"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Dialog associated with Answer.""" + short_answer: Optional["_models.AnswerSpan"] = rest_field( + name="answerSpan", visibility=["read", "create", "update", "delete", "query"] + ) + """Answer span object of QnA with respect to user's question.""" + + @overload def __init__( self, *, - questions: Optional[List[str]] = None, + questions: Optional[list[str]] = None, answer: Optional[str] = None, confidence: Optional[float] = None, qna_id: Optional[int] = None, source: Optional[str] = None, - metadata: Optional[Dict[str, str]] = None, + metadata: Optional[dict[str, str]] = None, dialog: Optional["_models.KnowledgeBaseAnswerDialog"] = None, short_answer: Optional["_models.AnswerSpan"] = None, - **kwargs: Any - ) -> None: - """ - :keyword questions: List of questions associated with the answer. - :paramtype questions: list[str] - :keyword answer: Answer text. - :paramtype answer: str - :keyword confidence: Answer confidence score, value ranges from 0 to 1. - :paramtype confidence: float - :keyword qna_id: ID of the QnA result. - :paramtype qna_id: int - :keyword source: Source of QnA result. - :paramtype source: str - :keyword metadata: Metadata associated with the answer, useful to categorize or filter question - answers. - :paramtype metadata: dict[str, str] - :keyword dialog: Dialog associated with Answer. - :paramtype dialog: ~azure.ai.language.questionanswering.models.KnowledgeBaseAnswerDialog - :keyword short_answer: Answer span object of QnA with respect to user's question. - :paramtype short_answer: ~azure.ai.language.questionanswering.models.AnswerSpan - """ - super().__init__(**kwargs) - self.questions = questions - self.answer = answer - self.confidence = confidence - self.qna_id = qna_id - self.source = source - self.metadata = metadata - self.dialog = dialog - self.short_answer = short_answer - - -class KnowledgeBaseAnswerContext(_serialization.Model): - """Context object with previous QnA's information. + ) -> None: ... - All required parameters must be populated in order to send to server. + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class KnowledgeBaseAnswerContext(_Model): + """Context object with previous QnA's information. :ivar previous_qna_id: Previous turn top answer result QnA ID. Required. :vartype previous_qna_id: int @@ -508,68 +506,75 @@ class KnowledgeBaseAnswerContext(_serialization.Model): :vartype previous_question: str """ - _validation = { - "previous_qna_id": {"required": True}, - } + previous_qna_id: int = rest_field(name="previousQnaId", visibility=["read", "create", "update", "delete", "query"]) + """Previous turn top answer result QnA ID. Required.""" + previous_question: Optional[str] = rest_field( + name="previousUserQuery", visibility=["read", "create", "update", "delete", "query"] + ) + """Previous user query.""" - _attribute_map = { - "previous_qna_id": {"key": "previousQnaId", "type": "int"}, - "previous_question": {"key": "previousUserQuery", "type": "str"}, - } + @overload + def __init__( + self, + *, + previous_qna_id: int, + previous_question: Optional[str] = None, + ) -> None: ... - def __init__(self, *, previous_qna_id: int, previous_question: Optional[str] = None, **kwargs: Any) -> None: + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: """ - :keyword previous_qna_id: Previous turn top answer result QnA ID. Required. - :paramtype previous_qna_id: int - :keyword previous_question: Previous user query. - :paramtype previous_question: str + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] """ - super().__init__(**kwargs) - self.previous_qna_id = previous_qna_id - self.previous_question = previous_question + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) -class KnowledgeBaseAnswerDialog(_serialization.Model): + +class KnowledgeBaseAnswerDialog(_Model): """Dialog associated with Answer. :ivar is_context_only: To mark if a prompt is relevant only with a previous question or not. If - true, do not include this QnA as search result for queries without context; otherwise, if - false, ignores context and includes this QnA in search result. + true, + do not include this QnA as search result for queries without context; + otherwise, if false, ignores context and includes this QnA in search result. :vartype is_context_only: bool :ivar prompts: List of prompts associated with the answer. :vartype prompts: list[~azure.ai.language.questionanswering.models.KnowledgeBaseAnswerPrompt] """ - _validation = { - "prompts": {"max_items": 20, "min_items": 0}, - } - - _attribute_map = { - "is_context_only": {"key": "isContextOnly", "type": "bool"}, - "prompts": {"key": "prompts", "type": "[KnowledgeBaseAnswerPrompt]"}, - } - + is_context_only: Optional[bool] = rest_field( + name="isContextOnly", visibility=["read", "create", "update", "delete", "query"] + ) + """To mark if a prompt is relevant only with a previous question or not. If true, + do not include this QnA as search result for queries without context; + otherwise, if false, ignores context and includes this QnA in search result.""" + prompts: Optional[list["_models.KnowledgeBaseAnswerPrompt"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """List of prompts associated with the answer.""" + + @overload def __init__( self, *, is_context_only: Optional[bool] = None, - prompts: Optional[List["_models.KnowledgeBaseAnswerPrompt"]] = None, - **kwargs: Any - ) -> None: + prompts: Optional[list["_models.KnowledgeBaseAnswerPrompt"]] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: """ - :keyword is_context_only: To mark if a prompt is relevant only with a previous question or not. - If true, do not include this QnA as search result for queries without context; otherwise, if - false, ignores context and includes this QnA in search result. - :paramtype is_context_only: bool - :keyword prompts: List of prompts associated with the answer. - :paramtype prompts: list[~azure.ai.language.questionanswering.models.KnowledgeBaseAnswerPrompt] + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] """ - super().__init__(**kwargs) - self.is_context_only = is_context_only - self.prompts = prompts + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) -class KnowledgeBaseAnswerPrompt(_serialization.Model): +class KnowledgeBaseAnswerPrompt(_Model): """Prompt for an answer. :ivar display_order: Index of the prompt - used in ordering of the prompts. @@ -580,67 +585,191 @@ class KnowledgeBaseAnswerPrompt(_serialization.Model): :vartype display_text: str """ - _validation = { - "display_text": {"max_length": 200}, - } - - _attribute_map = { - "display_order": {"key": "displayOrder", "type": "int"}, - "qna_id": {"key": "qnaId", "type": "int"}, - "display_text": {"key": "displayText", "type": "str"}, - } - + display_order: Optional[int] = rest_field( + name="displayOrder", visibility=["read", "create", "update", "delete", "query"] + ) + """Index of the prompt - used in ordering of the prompts.""" + qna_id: Optional[int] = rest_field(name="qnaId", visibility=["read", "create", "update", "delete", "query"]) + """QnA ID corresponding to the prompt.""" + display_text: Optional[str] = rest_field( + name="displayText", visibility=["read", "create", "update", "delete", "query"] + ) + """Text displayed to represent a follow up question prompt.""" + + @overload def __init__( self, *, display_order: Optional[int] = None, qna_id: Optional[int] = None, display_text: Optional[str] = None, - **kwargs: Any - ) -> None: + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class MatchingPolicy(_Model): + """Specify parameters for query matching. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + PrebuiltQueryMatchingPolicy + + :ivar kind: Kind of matching policy to be applied. Required. "Prebuilt" + :vartype kind: str or ~azure.ai.language.questionanswering.models.MatchingPolicyKind + """ + + __mapping__: dict[str, _Model] = {} + kind: str = rest_discriminator(name="kind", visibility=["read", "create", "update", "delete", "query"]) + """Kind of matching policy to be applied. Required. \"Prebuilt\"""" + + @overload + def __init__( + self, + *, + kind: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: """ - :keyword display_order: Index of the prompt - used in ordering of the prompts. - :paramtype display_order: int - :keyword qna_id: QnA ID corresponding to the prompt. - :paramtype qna_id: int - :keyword display_text: Text displayed to represent a follow up question prompt. - :paramtype display_text: str + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] """ - super().__init__(**kwargs) - self.display_order = display_order - self.qna_id = qna_id - self.display_text = display_text + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) -class MetadataFilter(_serialization.Model): +class MetadataFilter(_Model): """Find QnAs that are associated with the given list of metadata. - :ivar metadata: - :vartype metadata: list[JSON] - :ivar logical_operation: Operation used to join metadata filters. - :vartype logical_operation: str + :ivar metadata: Dictionary of string. + :vartype metadata: list[~azure.ai.language.questionanswering.models.MetadataRecord] + :ivar logical_operation: Operation used to join metadata filters. Known values are: "AND" and + "OR". + :vartype logical_operation: str or + ~azure.ai.language.questionanswering.models.LogicalOperationKind """ - _attribute_map = { - "metadata": {"key": "metadata", "type": "[object]"}, - "logical_operation": {"key": "logicalOperation", "type": "str"}, - } + metadata: Optional[list["_models.MetadataRecord"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Dictionary of string.""" + logical_operation: Optional[Union[str, "_models.LogicalOperationKind"]] = rest_field( + name="logicalOperation", visibility=["read", "create", "update", "delete", "query"] + ) + """Operation used to join metadata filters. Known values are: \"AND\" and \"OR\".""" + @overload def __init__( - self, *, metadata: Optional[List[JSON]] = None, logical_operation: Optional[str] = None, **kwargs: Any - ) -> None: + self, + *, + metadata: Optional[list["_models.MetadataRecord"]] = None, + logical_operation: Optional[Union[str, "_models.LogicalOperationKind"]] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: """ - :keyword metadata: - :paramtype metadata: list[JSON] - :keyword logical_operation: Operation used to join metadata filters. - :paramtype logical_operation: str + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] """ - super().__init__(**kwargs) - self.metadata = metadata - self.logical_operation = logical_operation + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) -class QueryFilters(_serialization.Model): + +class MetadataRecord(_Model): + """Object to provide the key value pair for each metadata. + + :ivar key: Metadata Key from Metadata dictionary used in the QnA. Required. + :vartype key: str + :ivar value: Metadata Value from Metadata dictionary used in the QnA. Required. + :vartype value: str + """ + + key: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Metadata Key from Metadata dictionary used in the QnA. Required.""" + value: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Metadata Value from Metadata dictionary used in the QnA. Required.""" + + @overload + def __init__( + self, + *, + key: str, + value: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class PrebuiltQueryMatchingPolicy(MatchingPolicy, discriminator="Prebuilt"): + """Represents fields for Prebuilt query matching. Prebuilt weights will be used for giving + preference to question and answer columns when quering AI search. + + :ivar kind: Matching policy kind. Required. Prebuilt weights will be used for giving preference + to question and answer columns when quering AI search + :vartype kind: str or ~azure.ai.language.questionanswering.models.PREBUILT + :ivar fields: List of fields to filter during query. For ex if only "questions" is used then + query will be filtered on that column. + :vartype fields: list[str or + ~azure.ai.language.questionanswering.models.MatchingPolicyFieldsType] + :ivar disable_full_match: Disabling full match on query. Enabling this will give preference to + qna pairs that have exact match. + :vartype disable_full_match: bool + """ + + kind: Literal[MatchingPolicyKind.PREBUILT] = rest_discriminator(name="kind", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Matching policy kind. Required. Prebuilt weights will be used for giving preference to question + and answer columns when quering AI search""" + fields: Optional[list[Union[str, "_models.MatchingPolicyFieldsType"]]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """List of fields to filter during query. For ex if only \"questions\" is used then query will be + filtered on that column.""" + disable_full_match: Optional[bool] = rest_field( + name="disableFullMatch", visibility=["read", "create", "update", "delete", "query"] + ) + """Disabling full match on query. Enabling this will give preference to qna pairs that have exact + match.""" + + @overload + def __init__( + self, + *, + fields: Optional[list[Union[str, "_models.MatchingPolicyFieldsType"]]] = None, + disable_full_match: Optional[bool] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, kind=MatchingPolicyKind.PREBUILT, **kwargs) + + +class QueryFilters(_Model): """filters over knowledge base. :ivar metadata_filter: Find QnAs that are associated with the given list of metadata. @@ -649,84 +778,131 @@ class QueryFilters(_serialization.Model): knowledge base. :vartype source_filter: list[str] :ivar logical_operation: Logical operation used to join metadata filter with source filter. - :vartype logical_operation: str + Known values are: "AND" and "OR". + :vartype logical_operation: str or + ~azure.ai.language.questionanswering.models.LogicalOperationKind """ - _attribute_map = { - "metadata_filter": {"key": "metadataFilter", "type": "MetadataFilter"}, - "source_filter": {"key": "sourceFilter", "type": "[str]"}, - "logical_operation": {"key": "logicalOperation", "type": "str"}, - } - + metadata_filter: Optional["_models.MetadataFilter"] = rest_field( + name="metadataFilter", visibility=["read", "create", "update", "delete", "query"] + ) + """Find QnAs that are associated with the given list of metadata.""" + source_filter: Optional[list[str]] = rest_field( + name="sourceFilter", visibility=["read", "create", "update", "delete", "query"] + ) + """Find QnAs that are associated with any of the given list of sources in + knowledge base.""" + logical_operation: Optional[Union[str, "_models.LogicalOperationKind"]] = rest_field( + name="logicalOperation", visibility=["read", "create", "update", "delete", "query"] + ) + """Logical operation used to join metadata filter with source filter. Known values are: \"AND\" + and \"OR\".""" + + @overload def __init__( self, *, metadata_filter: Optional["_models.MetadataFilter"] = None, - source_filter: Optional[List[str]] = None, - logical_operation: Optional[str] = None, - **kwargs: Any - ) -> None: - """ - :keyword metadata_filter: Find QnAs that are associated with the given list of metadata. - :paramtype metadata_filter: ~azure.ai.language.questionanswering.models.MetadataFilter - :keyword source_filter: Find QnAs that are associated with any of the given list of sources in - knowledge base. - :paramtype source_filter: list[str] - :keyword logical_operation: Logical operation used to join metadata filter with source filter. - :paramtype logical_operation: str - """ - super().__init__(**kwargs) - self.metadata_filter = metadata_filter - self.source_filter = source_filter - self.logical_operation = logical_operation - - -class ShortAnswerOptions(_serialization.Model): - """To configure Answer span prediction feature. + source_filter: Optional[list[str]] = None, + logical_operation: Optional[Union[str, "_models.LogicalOperationKind"]] = None, + ) -> None: ... - Variables are only populated by the server, and will be ignored when sending a request. + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class QueryPreferences(_Model): + """Additional properties to fine tune query results. + + :ivar scorer: To specify what scoring algorithm is preferred. Known values are: "Classic", + "Transformer", and "Semantic". + :vartype scorer: str or ~azure.ai.language.questionanswering.models.Scorer + :ivar matching_policy: Policy for controling exact query match behavior. + :vartype matching_policy: ~azure.ai.language.questionanswering.models.MatchingPolicy + """ - All required parameters must be populated in order to send to server. + scorer: Optional[Union[str, "_models.Scorer"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """To specify what scoring algorithm is preferred. Known values are: \"Classic\", \"Transformer\", + and \"Semantic\".""" + matching_policy: Optional["_models.MatchingPolicy"] = rest_field( + name="matchingPolicy", visibility=["read", "create", "update", "delete", "query"] + ) + """Policy for controling exact query match behavior.""" + + @overload + def __init__( + self, + *, + scorer: Optional[Union[str, "_models.Scorer"]] = None, + matching_policy: Optional["_models.MatchingPolicy"] = None, + ) -> None: ... - :ivar enable: Enable or disable Answer Span prediction. Required. Default value is True. + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class ShortAnswerOptions(_Model): + """To configure Answer span prediction feature. + + :ivar enable: Enable or disable Answer Span prediction. Required. :vartype enable: bool :ivar confidence_threshold: Minimum threshold score required to include an answer span, value - ranges from 0 to 1. + ranges from 0 + to 1. :vartype confidence_threshold: float :ivar top: Number of Top answers to be considered for span prediction from 1 to 10. :vartype top: int """ - _validation = { - "enable": {"required": True, "constant": True}, - "confidence_threshold": {"maximum": 1, "minimum": 0}, - "top": {"maximum": 10, "minimum": 1}, - } - - _attribute_map = { - "enable": {"key": "enable", "type": "bool"}, - "confidence_threshold": {"key": "confidenceScoreThreshold", "type": "float"}, - "top": {"key": "topAnswersWithSpan", "type": "int"}, - } - - enable = True - + enable: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Enable or disable Answer Span prediction. Required.""" + confidence_threshold: Optional[float] = rest_field( + name="confidenceScoreThreshold", visibility=["read", "create", "update", "delete", "query"] + ) + """Minimum threshold score required to include an answer span, value ranges from 0 + to 1.""" + top: Optional[int] = rest_field( + name="topAnswersWithSpan", visibility=["read", "create", "update", "delete", "query"] + ) + """Number of Top answers to be considered for span prediction from 1 to 10.""" + + @overload def __init__( - self, *, confidence_threshold: Optional[float] = None, top: Optional[int] = None, **kwargs: Any - ) -> None: + self, + *, + enable: bool, + confidence_threshold: Optional[float] = None, + top: Optional[int] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: """ - :keyword confidence_threshold: Minimum threshold score required to include an answer span, - value ranges from 0 to 1. - :paramtype confidence_threshold: float - :keyword top: Number of Top answers to be considered for span prediction from 1 to 10. - :paramtype top: int + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] """ - super().__init__(**kwargs) - self.confidence_threshold = confidence_threshold - self.top = top + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) -class TextAnswer(_serialization.Model): + +class TextAnswer(_Model): """Represents answer result. :ivar answer: Answer. @@ -743,19 +919,24 @@ class TextAnswer(_serialization.Model): :vartype length: int """ - _validation = { - "confidence": {"maximum": 1, "minimum": 0}, - } - - _attribute_map = { - "answer": {"key": "answer", "type": "str"}, - "confidence": {"key": "confidenceScore", "type": "float"}, - "id": {"key": "id", "type": "str"}, - "short_answer": {"key": "answerSpan", "type": "AnswerSpan"}, - "offset": {"key": "offset", "type": "int"}, - "length": {"key": "length", "type": "int"}, - } - + answer: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Answer.""" + confidence: Optional[float] = rest_field( + name="confidenceScore", visibility=["read", "create", "update", "delete", "query"] + ) + """answer confidence score, value ranges from 0 to 1.""" + id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """record ID.""" + short_answer: Optional["_models.AnswerSpan"] = rest_field( + name="answerSpan", visibility=["read", "create", "update", "delete", "query"] + ) + """Answer span object with respect to user's question.""" + offset: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The sentence offset from the start of the document.""" + length: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The length of the sentence.""" + + @overload def __init__( self, *, @@ -765,35 +946,21 @@ def __init__( short_answer: Optional["_models.AnswerSpan"] = None, offset: Optional[int] = None, length: Optional[int] = None, - **kwargs: Any - ) -> None: - """ - :keyword answer: Answer. - :paramtype answer: str - :keyword confidence: answer confidence score, value ranges from 0 to 1. - :paramtype confidence: float - :keyword id: record ID. - :paramtype id: str - :keyword short_answer: Answer span object with respect to user's question. - :paramtype short_answer: ~azure.ai.language.questionanswering.models.AnswerSpan - :keyword offset: The sentence offset from the start of the document. - :paramtype offset: int - :keyword length: The length of the sentence. - :paramtype length: int - """ - super().__init__(**kwargs) - self.answer = answer - self.confidence = confidence - self.id = id - self.short_answer = short_answer - self.offset = offset - self.length = length - - -class TextDocument(_serialization.Model): - """Represent input text record to be queried. + ) -> None: ... - All required parameters must be populated in order to send to server. + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class TextDocument(_Model): + """Represent input text record to be queried. :ivar id: Unique identifier for the text record. Required. :vartype id: str @@ -801,23 +968,25 @@ class TextDocument(_serialization.Model): :vartype text: str """ - _validation = { - "id": {"required": True}, - "text": {"required": True}, - } + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Unique identifier for the text record. Required.""" + text: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Text contents of the record. Required.""" - _attribute_map = { - "id": {"key": "id", "type": "str"}, - "text": {"key": "text", "type": "str"}, - } + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + text: str, + ) -> None: ... - def __init__(self, *, id: str, text: str, **kwargs: Any) -> None: # pylint: disable=redefined-builtin + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: """ - :keyword id: Unique identifier for the text record. Required. - :paramtype id: str - :keyword text: Text contents of the record. Required. - :paramtype text: str + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] """ - super().__init__(**kwargs) - self.id = id - self.text = text + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_patch.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_patch.py index 402652a8772e..96fa98264eb2 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_patch.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/azure/ai/language/questionanswering/models/_patch.py @@ -1,47 +1,85 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- """Customize generated code here. Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize """ -from typing import List, Optional, Tuple, Union, cast, Any +from typing import List, Optional, Tuple, Union, cast, Any, MutableMapping, overload, Mapping from ._models import ( MetadataFilter as MetadataFilterGenerated, AnswersFromTextOptions as AnswersFromTextOptionsGenerated, TextDocument, - JSON, + MetadataRecord ) +JSON = MutableMapping[str, Any] + class MetadataFilter(MetadataFilterGenerated): """Find QnAs that are associated with the given list of metadata. :ivar metadata: - :vartype metadata: list[tuple[str, str]] + :vartype metadata: list[tuple[str, str]] or list[~azure.ai.language.questionanswering.models.MetadataRecord] :ivar logical_operation: Operation used to join metadata filters. Possible values include: "AND", "OR". :vartype logical_operation: str """ - + @overload def __init__( self, *, metadata: Optional[List[Tuple[str, str]]] = None, logical_operation: Optional[str] = None, **kwargs: Any - ) -> None: - """ - :keyword metadata: - :paramtype metadata: list[tuple[str, str]] - :keyword logical_operation: Operation used to join metadata filters. Possible values include: - "AND", "OR". - :paramtype logical_operation: str + ) -> None: # pragma: no cover - overload definition + """Overload accepting list of (key, value) tuples.""" + + @overload + def __init__( + self, + *, + metadata: Optional[List[MetadataRecord]] = None, + logical_operation: Optional[str] = None, + **kwargs: Any + ) -> None: # pragma: no cover - overload definition + """Overload accepting list of MetadataRecord objects.""" + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: # pragma: no cover - overload definition + """Overload accepting raw JSON mapping used during deserialization.""" + + def __init__(self, *args: Any, **kwargs: Any) -> None: # noqa: D401 - docstring covered by overloads + """Create a MetadataFilter. + + Accepts either keyword-only parameters (``metadata``/``logical_operation``) or a single + raw JSON ``mapping`` positional argument used internally for deserialization. """ - # pylint:disable=useless-super-delegation - super().__init__(metadata=cast(Optional[List[JSON]], metadata), logical_operation=logical_operation, **kwargs) + # Mapping form: pass straight through to generated model + if args and isinstance(args[0], Mapping): + super().__init__(*args, **kwargs) + return + + metadata = kwargs.pop("metadata", None) + logical_operation = kwargs.pop("logical_operation", None) + converted: Optional[List[MetadataRecord]] + if metadata is not None: + converted = [] + for item in metadata: + if isinstance(item, tuple): + converted.append(MetadataRecord(key=item[0], value=item[1])) + elif isinstance(item, MetadataRecord): + converted.append(item) + else: + raise TypeError( + f"metadata items must be tuples or MetadataRecord objects, got {type(item)}" + ) + else: + converted = None + super().__init__(metadata=converted, logical_operation=logical_operation, **kwargs) class AnswersFromTextOptions(AnswersFromTextOptionsGenerated): """The question and text record parameters to answer. @@ -77,17 +115,18 @@ def __init__( :paramtype language: str """ super().__init__( - question=question, text_documents=cast(List[TextDocument], text_documents), language=language, **kwargs + question=question, + text_documents=cast(List[TextDocument], text_documents), + language=language, + string_index_type="UnicodeCodePoint", + **kwargs ) - self.string_index_type = "UnicodeCodePoint" - self._attribute_map.update({"string_index_type": {"key": "stringIndexType", "type": "str"}}) __all__: List[str] = [ "MetadataFilter", "AnswersFromTextOptions", -] # Add all objects you want publicly available to users at this package level - +] def patch_sdk(): """Do not remove from this file. diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/dev_requirements.txt b/sdk/cognitivelanguage/azure-ai-language-questionanswering/dev_requirements.txt index d85e980ba5ab..06543e63230c 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/dev_requirements.txt +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/dev_requirements.txt @@ -1,4 +1,4 @@ -e ../../../eng/tools/azure-sdk-tools ../../core/azure-core -e ../../identity/azure-identity -aiohttp>=3.0 +aiohttp>=3.0 \ No newline at end of file diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/migration_guide.md b/sdk/cognitivelanguage/azure-ai-language-questionanswering/migration_guide.md index db6784d0936f..1b526307c5f9 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/migration_guide.md +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/migration_guide.md @@ -477,6 +477,16 @@ async with QuestionAnsweringClient(endpoint=endpoint, credential=credential) as ``` +## Migrating to the New SDK Architecture (Future Release) + +In an upcoming release, the Question Answering SDK will be restructured to separate authoring and inference operations into distinct client libraries. This separation will provide better modularity and clearer separation of concerns. + +### Key Changes in the New Architecture + +#### Separated Packages +- **Inference operations**: Will remain in `azure-ai-language-questionanswering` +- **Authoring operations**: Will move to a separate authoring package + ## Additional Samples The new `azure-ai-language-questionanswering` has new capabilities not supported by the old client library, you can @@ -491,3 +501,4 @@ see additional samples [here][qna_samples]. [aiohttp]: https://pypi.org/project/aiohttp/ [azure_core_transport]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/CLIENT_LIBRARY_DEVELOPER.md#transport [qna_samples]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples +[azure_sdk_releases]: https://github.com/Azure/azure-sdk-for-python/releases diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/pyproject.toml b/sdk/cognitivelanguage/azure-ai-language-questionanswering/pyproject.toml new file mode 100644 index 000000000000..6195829bfcb2 --- /dev/null +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/pyproject.toml @@ -0,0 +1,60 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +[build-system] +requires = ["setuptools>=77.0.3", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "azure-ai-language-questionanswering" +authors = [ + { name = "Microsoft Corporation", email = "azpysdkhelp@microsoft.com" }, +] +description = "Microsoft Corporation Azure Ai Language Questionanswering Client Library for Python" +license = "MIT" +classifiers = [ + "Development Status :: 4 - Beta", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] +requires-python = ">=3.9" +keywords = ["azure", "azure sdk"] + +dependencies = [ + "isodate>=0.6.1", + "azure-core>=1.35.0", + "typing-extensions>=4.6.0", +] +dynamic = [ +"version", "readme" +] + +[project.urls] +repository = "https://github.com/Azure/azure-sdk-for-python" + +[tool.setuptools.dynamic] +version = {attr = "azure.ai.language.questionanswering._version.VERSION"} +readme = {file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown"} + +[tool.setuptools.packages.find] +exclude = [ + "tests*", + "samples*", + "doc*", + "azure", + "azure.ai", + "azure.ai.language", +] + +[tool.setuptools.package-data] +pytyped = ["py.typed"] diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/README.md b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/README.md index d5b9730dcab5..fd48dd0edba3 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/README.md +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/README.md @@ -9,63 +9,63 @@ products: urlFragment: languagequestionanswering-samples --- -# Samples for Language QuestionAnswering client library for Python +# Samples for Azure Language Question Answering (Inference) -Question Answering is a cloud-based API service that lets you create a conversational question-and-answer layer over your existing data. Use it to build a knowledge base by extracting questions and answers from your semi-structured content, including FAQ, manuals, and documents. Answer users' questions with the best answers from the QnAs in your knowledge base—automatically. Your knowledge base gets smarter, too, as it continually learns from user behavior. +These samples demonstrate how to use the runtime Question Answering features to query an existing knowledge base +or ad‑hoc text content. Authoring (project creation / deployment / source management) samples have been moved +to a separate location and are intentionally not referenced here. -These code samples show common scenario operations with the Azure Language QuestionAnswering client library. -You can authenticate your client with a QuestionAnswering API key. +## Samples Included -These sample programs show common scenarios for the QuestionAnswering client's offerings. +| File | Description | +|------|-------------| +| `sample_query_knowledgebase.py` / `async_samples/sample_query_knowledgebase_async.py` | Ask a question against a deployed knowledge base project (flattened parameters) | +| `sample_chat.py` / `async_samples/sample_chat_async.py` | Ask a follow‑up contextual question using `answer_context` | +| `sample_query_text.py` / `async_samples/sample_query_text_async.py` | Ask a question over ad‑hoc text documents (options object pattern) | -|**File Name**|**Description**| -|-------------|---------------| -|[sample_query_knowledgebase.py][query_knowledgebase] and [sample_query_knowledgebase_async.py][query_knowledgebase_async]|Ask a question from a knowledge base| -|[sample_chat.py][chat] and [sample_chat_async.py][chat_async]|Ask a follow-up question (chit-chat)| -|[sample_query_text.py][query_text] and [sample_query_text_async.py][query_text_async]|Ask a question from provided text data| -|[sample_create_and_deploy_project.py][create_and_deploy] and [sample_create_and_deploy_project_async.py][create_and_deploy_async]|sample for creating and deploying a Qna project| -|[sample_export_import_project.py][export_import] and [sample_export_import_project_async.py][export_import_async]|sample for exporting and importing a project| -|[sample_update_knowledge_sources.py][add_knowledge_sources] and [sample_update_knowledge_sources_async.py][add_knowledge_sources_async]|sample for adding knowledge sources| +> Note: The deprecated wrapper methods `client.get_answers` and `client.get_answers_from_text` +> have been replaced in these samples by the recommended operations group access: +> `client.get_answers` and `client.get_answers_from_text`. +## Prerequisites +- Python 3.9+ (match the package's `python_requires`) +- An [Azure subscription][azure_subscription] +- A deployed Language Question Answering resource (project + deployment for knowledge base queries) -### Prerequisites +## Environment Variables -* Python 3.7 or later is required to use this package. -* An [Azure subscription][azure_subscription] -* A [Language Service][language_service] resource +Set the following environment variables before running a sample: +Required (all runtime samples): +- `AZURE_QUESTIONANSWERING_ENDPOINT` – your resource endpoint +- `AZURE_QUESTIONANSWERING_KEY` – the API key -## Setup +Additional for knowledge base & chat samples: +- `AZURE_QUESTIONANSWERING_PROJECT` – project (knowledge base) name +- `AZURE_QUESTIONANSWERING_DEPLOYMENT` – deployment name (if omitted, samples default to `production`) + +## Install the Library -1. Install the Azure QuestionAnswering client library for Python with [pip][pip]: ```bash pip install azure-ai-language-questionanswering ``` -2. Clone or download this sample repository -3. Open the sample folder in Visual Studio Code or your IDE of choice. -## Running the samples +## Run -1. Open a terminal window and `cd` to the directory that the samples are saved in. -2. Set the environment variables specified in the sample file you wish to run. -3. Follow the usage described in the file, e.g. `python sample_chat.py` +```bash +python sample_query_knowledgebase.py +python sample_chat.py +python sample_query_text.py +# Async examples: +python async_samples/sample_query_knowledgebase_async.py +``` +Ensure you are in this `samples` directory (or adjust paths accordingly). -[query_knowledgebase]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_query_knowledgebase.py -[query_knowledgebase_async]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_query_knowledgebase_async.py -[chat]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_chat.py -[chat_async]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_chat_async.py -[query_text]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_query_text.py -[query_text_async]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_query_text_async.py +## Authentication -[create_and_deploy]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_create_and_deploy_project.py -[create_and_deploy_async]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_create_and_deploy_project_async.py -[export_import]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_export_import_project.py -[export_import_async]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_export_import_project_async.py -[add_knowledge_sources]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_update_knowledge_sources.py -[add_knowledge_sources_async]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_update_knowledge_sources_async.py +These samples use `AzureKeyCredential`. For AAD / `DefaultAzureCredential` usage, see the main package documentation. -[language_service]: https://ms.portal.azure.com/#create/Microsoft.CognitiveServicesTextAnalytics -[pip]: https://pypi.org/project/pip/ [azure_subscription]: https://azure.microsoft.com/free/ +[language_service]: https://ms.portal.azure.com/#create/Microsoft.CognitiveServicesTextAnalytics diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_chat_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_chat_async.py index 4c86ee7a781e..1e0781227db4 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_chat_async.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_chat_async.py @@ -8,17 +8,13 @@ FILE: sample_chat_async.py DESCRIPTION: - This sample demonstrates how to ask a follow-up question (chit-chat) from a knowledge base. + Async follow-up (contextual) question with answer_context. USAGE: python sample_chat_async.py - - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. - 3) AZURE_QUESTIONANSWERING_PROJECT - the name of a knowledge base project. """ +from __future__ import annotations import asyncio @@ -27,62 +23,72 @@ async def sample_chit_chat(): import os from azure.core.credentials import AzureKeyCredential from azure.ai.language.questionanswering.aio import QuestionAnsweringClient - from azure.ai.language.questionanswering import models as qna + from azure.ai.language.questionanswering.models import ( + AnswersOptions, + ShortAnswerOptions, + KnowledgeBaseAnswerContext, + ) endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - knowledge_base_project = os.environ["AZURE_QUESTIONANSWERING_PROJECT"] + project = os.environ["AZURE_QUESTIONANSWERING_PROJECT"] + deployment = os.environ.get("AZURE_QUESTIONANSWERING_DEPLOYMENT", "production") client = QuestionAnsweringClient(endpoint, AzureKeyCredential(key)) async with client: - first_question="How long should my Surface battery last?" - output = await client.get_answers( + first_question = "How long should my Surface battery last?" + first_options = AnswersOptions( question=first_question, top=3, confidence_threshold=0.2, include_unstructured_sources=True, - short_answer_options=qna.ShortAnswerOptions( - confidence_threshold=0.2, - top=1 - ), - project_name=knowledge_base_project, - deployment_name="test" + short_answer_options=ShortAnswerOptions(enable=True, confidence_threshold=0.2, top=1), + ) + first_output = await client.get_answers( + first_options, + project_name=project, + deployment_name=deployment, ) - if output.answers: - best_candidate = [a for a in output.answers if a.confidence and a.confidence > 0.7][0] - print("Q: {}".format(first_question)) - print("A: {}".format(best_candidate.answer)) - else: - print(f"No answers returned from question '{first_question}'") + best_candidate = next( + (a for a in (first_output.answers or []) if a.confidence and a.confidence > 0.7), + None, + ) + if not best_candidate: + print(f"No answers for '{first_question}'") return - if best_candidate.qna_id: - followup_question = "How long it takes to charge Surface?" + print(f"Q: {first_question}") + print(f"A: {best_candidate.answer}") - output = await client.get_answers( + if best_candidate.qna_id: + followup_question = "How long does it take to charge a Surface?" + followup_options = AnswersOptions( question=followup_question, top=3, confidence_threshold=0.2, - answer_context=qna.KnowledgeBaseAnswerContext( + answer_context=KnowledgeBaseAnswerContext( previous_question=first_question, - previous_qna_id=best_candidate.qna_id - ), - short_answer_options=qna.ShortAnswerOptions( - confidence_threshold=0.2, - top=1 + previous_qna_id=best_candidate.qna_id, ), + short_answer_options=ShortAnswerOptions(enable=True, confidence_threshold=0.2, top=1), include_unstructured_sources=True, - project_name=knowledge_base_project, - deployment_name="test" ) - if output.answers: - print(u"Q: {}".format(followup_question)) - print(u"A: {}".format(output.answers[0].answer)) + follow_output = await client.get_answers( + followup_options, + project_name=project, + deployment_name=deployment, + ) + follow_best = next( + (a for a in (follow_output.answers or []) if a.confidence and a.confidence > 0.2), + None, + ) + if follow_best: + print(f"Q: {followup_question}") + print(f"A: {follow_best.answer}") else: - print(f"No answers returned from question '{followup_question}'") - + print(f"No answers for follow-up '{followup_question}'") # [END chit_chat_async] -if __name__ == '__main__': +if __name__ == "__main__": asyncio.run(sample_chit_chat()) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_query_knowledgebase_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_query_knowledgebase_async.py index e0ac42060591..33c065c0d38c 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_query_knowledgebase_async.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_query_knowledgebase_async.py @@ -8,17 +8,13 @@ FILE: sample_query_knowledgebase_async.py DESCRIPTION: - This sample demonstrates how to ask a question from a knowledge base. + Async knowledge base query using flattened parameters. USAGE: python sample_query_knowledgebase_async.py - - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. - 3) AZURE_QUESTIONANSWERING_PROJECT - the name of a knowledge base project. """ +from __future__ import annotations import asyncio @@ -27,36 +23,42 @@ async def sample_query_knowledgebase(): import os from azure.core.credentials import AzureKeyCredential from azure.ai.language.questionanswering.aio import QuestionAnsweringClient - from azure.ai.language.questionanswering import models as qna + from azure.ai.language.questionanswering.models import ( + AnswersOptions, + ShortAnswerOptions, + ) endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - knowledge_base_project = os.environ["AZURE_QUESTIONANSWERING_PROJECT"] + project = os.environ["AZURE_QUESTIONANSWERING_PROJECT"] + deployment = os.environ.get("AZURE_QUESTIONANSWERING_DEPLOYMENT", "production") client = QuestionAnsweringClient(endpoint, AzureKeyCredential(key)) async with client: - question="How long should my Surface battery last?" - output = await client.get_answers( + question = "How long should my Surface battery last?" + options = AnswersOptions( question=question, top=3, confidence_threshold=0.2, include_unstructured_sources=True, - short_answer_options=qna.ShortAnswerOptions( - confidence_threshold=0.2, - top=1 - ), - project_name=knowledge_base_project, - deployment_name="test" + short_answer_options=ShortAnswerOptions(enable=True, confidence_threshold=0.2, top=1), + ) + output = await client.get_answers( + options, + project_name=project, + deployment_name=deployment, + ) + best_candidate = next( + (a for a in (output.answers or []) if a.confidence and a.confidence > 0.7), + None, ) - if output.answers: - best_candidate = [a for a in output.answers if a.confidence and a.confidence > 0.7][0] - print("Q: {}".format(question)) - print("A: {}".format(best_candidate.answer)) + if best_candidate: + print(f"Q: {question}") + print(f"A: {best_candidate.answer}") else: - print(f"No answers returned from question '{question}'") - + print(f"No answers for '{question}'") # [END query_knowledgebase_async] -if __name__ == '__main__': +if __name__ == "__main__": asyncio.run(sample_query_knowledgebase()) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_query_text_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_query_text_async.py index 0c17315a3589..05e9b9b03254 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_query_text_async.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/async_samples/sample_query_text_async.py @@ -8,15 +8,13 @@ FILE: sample_query_text_async.py DESCRIPTION: - This sample demonstrates how to ask a question from supplied text data. + Async ad-hoc text question (options object pattern). USAGE: python sample_query_text_async.py - - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. """ + +from __future__ import annotations import asyncio @@ -25,34 +23,50 @@ async def sample_query_text(): import os from azure.core.credentials import AzureKeyCredential from azure.ai.language.questionanswering.aio import QuestionAnsweringClient - from azure.ai.language.questionanswering import models as qna + from azure.ai.language.questionanswering.models import ( + AnswersFromTextOptions, + TextDocument, + ) endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] key = os.environ["AZURE_QUESTIONANSWERING_KEY"] client = QuestionAnsweringClient(endpoint, AzureKeyCredential(key)) async with client: - question = "How long it takes to charge surface?" - input = qna.AnswersFromTextOptions( + question = "How long does it take to charge a Surface?" + options = AnswersFromTextOptions( question=question, text_documents=[ - "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. " + - "The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface.", - ] + TextDocument( + id="doc1", + text=( + "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully " + "from an empty state. It can take longer if you're using your Surface for power-intensive " + "activities." + ), + ), + TextDocument( + id="doc2", + text=( + "You can use the USB port on your Surface Pro 4 power supply to charge other devices while " + "your Surface charges. The USB port is only for charging, not data transfer." + ), + ), + ], ) - output = await client.get_answers_from_text(input) - if output.answers: - best_answer = [a for a in output.answers if a.confidence and a.confidence > 0.9][0] - print("Q: {}".format(question)) - print("A: {}".format(best_answer.answer)) + output = await client.get_answers_from_text(options) + best_answer = next( + (a for a in (output.answers or []) if a.confidence and a.confidence > 0.9), + None, + ) + if best_answer: + print(f"Q: {question}") + print(f"A: {best_answer.answer}") else: - print(f"No answers returned from question '{question}'") - + print(f"No answers for '{question}'") # [END query_text_async] -if __name__ == '__main__': +if __name__ == "__main__": asyncio.run(sample_query_text()) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_create_and_deploy_project_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_create_and_deploy_project_async.py deleted file mode 100644 index d340e6f0ca55..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_create_and_deploy_project_async.py +++ /dev/null @@ -1,109 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -""" -FILE: sample_create_and_deploy_project_async.py - -DESCRIPTION: - This sample demonstrates how to create and deploy a Qna project. - -USAGE: - python sample_create_and_deploy_project_async.py - - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. -""" - -import asyncio - -async def sample_create_and_deploy_project_async(): - # [START create_and_deploy_project] - import os - from azure.core.credentials import AzureKeyCredential - from azure.ai.language.questionanswering.authoring.aio import AuthoringClient - - # get service secrets - endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] - key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - - # create client - client = AuthoringClient(endpoint, AzureKeyCredential(key)) - async with client: - - # create project - project_name = "IssacNewton" - project = await client.create_project( - project_name=project_name, - options={ - "description": "biography of Sir Issac Newton", - "language": "en", - "multilingualResource": True, - "settings": { - "defaultAnswer": "no answer" - } - }) - - print("view created project info:") - print("\tname: {}".format(project["projectName"])) - print("\tlanguage: {}".format(project["language"])) - print("\tdescription: {}".format(project["description"])) - - # list projects - print("find created project ..") - qna_projects = client.list_projects() - async for p in qna_projects: - if p["projectName"] == project_name: - print("project: {}".format(p["projectName"])) - print("\tlanguage: {}".format(p["language"])) - print("\tdescription: {}".format(p["description"])) - - # update sources (REQUIRED TO DEPLOY PROJECT) - update_sources_poller = await client.begin_update_sources( - project_name=project_name, - sources=[ - { - "op": "add", - "value": { - "displayName": "Issac Newton Bio", - "sourceUri": "https://wikipedia.org/wiki/Isaac_Newton", - "sourceKind": "url" - } - } - ] - ) - sources = await update_sources_poller.result() - - # list sources - print("list project sources") - async for source in sources: - print("source name: {}".format(source.get("displayName", "N/A"))) - print("\tsource: {}".format(source["source"])) - print("\tsource Uri: {}".format(source.get("sourceUri", "N/A"))) - print("\tsource kind: {}".format(source["sourceKind"])) - - # deploy project - deployment_poller = await client.begin_deploy_project( - project_name=project_name, - deployment_name="production" - ) - deployment = await deployment_poller.result() - print(f"Deployment successfully created under {deployment['deploymentName']}.") - - # list all deployments - deployments = client.list_deployments( - project_name=project_name - ) - - print("view project deployments") - async for d in deployments: - print(d) - - # [END create_and_deploy_project] - -if __name__ == '__main__': - loop = asyncio.get_event_loop() - loop.run_until_complete(sample_create_and_deploy_project_async()) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_export_import_project_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_export_import_project_async.py deleted file mode 100644 index 03d3a4ecaff6..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_export_import_project_async.py +++ /dev/null @@ -1,84 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -""" -FILE: sample_export_import_project_async.py - -DESCRIPTION: - This sample demonstrates how to export and import a Qna project. - -USAGE: - python sample_export_import_project_async.py - - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. - 3) AZURE_QUESTIONANSWERING_PROJECT - your existing Question Answering project to export/import. -""" - -import asyncio - - -async def sample_export_import_project_async(): - # [START export_import_project] - import os - import io - import zipfile - from azure.core.rest import HttpRequest - from azure.core.credentials import AzureKeyCredential - from azure.ai.language.questionanswering.authoring.aio import AuthoringClient - - # get service secrets - endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] - key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - project_name = os.environ["AZURE_QUESTIONANSWERING_PROJECT"] - - # create client - client = AuthoringClient(endpoint, AzureKeyCredential(key)) - async with client: - - # export - export_format = "json" - export_poller = await client.begin_export( - project_name=project_name, - file_format=export_format - ) - export_result = await export_poller.result() - export_url = export_result["resultUrl"] - request = HttpRequest("GET", export_url) - exported_project = None - - if export_format == "json": - response = await client.send_request(request) - exported_project = response.json() - elif export_format == "excel" or export_format == "tsv": - response = await client.send_request(request, stream=True) - exported_project = zipfile.ZipFile(io.BytesIO(await response.read())) - exported_project.extractall("./ExportedProject") - exported_project.close() - print(f"{export_format} project files written to ./ExportedProject.") - return - - # import project - import_poller = await client.begin_import_assets( - project_name=f"{project_name}-imported", - options=exported_project - ) - await import_poller.result() - - # list projects - print("view all qna projects:") - qna_projects = client.list_projects() - async for p in qna_projects: - if p["projectName"] == project_name: - print("project: {}".format(p["projectName"])) - print("\tlanguage: {}".format(p["language"])) - print("\tdescription: {}".format(p["description"])) - - # [END export_import_project] - -if __name__ == '__main__': - asyncio.run(sample_export_import_project_async()) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_update_knowledge_sources_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_update_knowledge_sources_async.py deleted file mode 100644 index 3e152ccf828a..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/async_samples/sample_update_knowledge_sources_async.py +++ /dev/null @@ -1,127 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -""" -FILE: sample_update_knowledge_sources_async.py - -DESCRIPTION: - This sample demonstrates how to update Qna project knowledge sources. - -USAGE: - python sample_update_knowledge_sources_async.py - - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. -""" - -import asyncio - -async def sample_update_knowledge_sources_async(): - # [START update_knowledge_sources] - import os - from azure.core.credentials import AzureKeyCredential - from azure.ai.language.questionanswering.authoring.aio import AuthoringClient - - # get service secrets - endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] - key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - - # create client - client = AuthoringClient(endpoint, AzureKeyCredential(key)) - async with client: - - # create project - project_name = "Microsoft" - await client.create_project( - project_name=project_name, - options={ - "description": "test project for some Microsoft QnAs", - "language": "en", - "multilingualResource": True, - "settings": { - "defaultAnswer": "no answer" - } - }) - - # sources - sources_poller = await client.begin_update_sources( - project_name=project_name, - sources=[{ - "op": "add", - "value": { - "displayName": "MicrosoftFAQ", - "source": "https://www.microsoft.com/en-in/software-download/faq", - "sourceUri": "https://www.microsoft.com/en-in/software-download/faq", - "sourceKind": "url", - "contentStructureKind": "unstructured", - "refresh": False - } - }] - ) - sources = await sources_poller.result() # wait until done - async for item in sources: - print("source name: {}".format(item.get("displayName", "N/A"))) - print("\tsource: {}".format(item["source"])) - print("\tsource uri: {}".format(item.get("sourceUri", "N/A"))) - print("\tsource kind: {}".format(item["sourceKind"])) - - # qnas - qna_poller = await client.begin_update_qnas( - project_name=project_name, - qnas=[{ - "op": "add", - "value": { - "questions": [ - "What is the easiest way to use azure services in my .NET project?" - ], - "answer": "Using Microsoft's Azure SDKs" - } - }] - ) - qnas = await qna_poller.result() - async for item in qnas: - print("qna: {}".format(item["id"])) - print("\tquestions:") - for question in item["questions"]: - print("\t\t{}".format(question)) - print("\tanswer: {}".format(item["answer"])) - - # synonyms - await client.update_synonyms( - project_name=project_name, - synonyms={ - "value": [ - { - "alterations": [ - "qnamaker", - "qna maker" - ] - }, - { - "alterations": [ - "qna", - "question and answer" - ] - } - ] - } - ) - synonyms = client.list_synonyms( - project_name=project_name - ) - async for item in synonyms: - print("synonyms:") - print("\talterations:") - for alt in item["alterations"]: - print("\t\t{}".format(alt)) - print('') - - # [END update_knowledge_sources] - -if __name__ == '__main__': - loop = asyncio.get_event_loop() - loop.run_until_complete(sample_update_knowledge_sources_async()) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_create_and_deploy_project.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_create_and_deploy_project.py deleted file mode 100644 index 831927120f2d..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_create_and_deploy_project.py +++ /dev/null @@ -1,107 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -""" -FILE: sample_create_and_deploy_project.py - -DESCRIPTION: - This sample demonstrates how to create and deploy a Qna project. - -USAGE: - python sample_create_and_deploy_project.py - - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. -""" - -def sample_create_and_deploy_project(): - # [START create_and_deploy_project] - import os - from azure.core.credentials import AzureKeyCredential - from azure.ai.language.questionanswering.authoring import AuthoringClient - - # get service secrets - endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] - key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - - # create client - client = AuthoringClient(endpoint, AzureKeyCredential(key)) - with client: - - # create project - project_name = "IssacNewton" - project = client.create_project( - project_name=project_name, - options={ - "description": "biography of Sir Issac Newton", - "language": "en", - "multilingualResource": True, - "settings": { - "defaultAnswer": "no answer" - } - }) - - print("view created project info:") - print("\tname: {}".format(project["projectName"])) - print("\tlanguage: {}".format(project["language"])) - print("\tdescription: {}".format(project["description"])) - - # list projects - print("find created project ..") - qna_projects = client.list_projects() - for p in qna_projects: - if p["projectName"] == project_name: - print("project: {}".format(p["projectName"])) - print("\tlanguage: {}".format(p["language"])) - print("\tdescription: {}".format(p["description"])) - - # update sources (REQUIRED TO DEPLOY PROJECT) - update_sources_poller = client.begin_update_sources( - project_name=project_name, - sources=[ - { - "op": "add", - "value": { - "displayName": "Issac Newton Bio", - "sourceUri": "https://wikipedia.org/wiki/Isaac_Newton", - "sourceKind": "url" - } - } - ] - ) - sources = update_sources_poller.result() - - # list sources - print("list project sources") - for source in sources: - print("source name: {}".format(source.get("displayName", "N/A"))) - print("\tsource: {}".format(source["source"])) - print("\tsource Uri: {}".format(source.get("sourceUri", "N/A"))) - print("\tsource kind: {}".format(source["sourceKind"])) - - # deploy project - deployment_poller = client.begin_deploy_project( - project_name=project_name, - deployment_name="production" - ) - deployment = deployment_poller.result() - print(f"Deployment successfully created under {deployment['deploymentName']}.") - - # list all deployments - deployments = client.list_deployments( - project_name=project_name - ) - - print("view project deployments") - for d in deployments: - print(d) - - # [END create_and_deploy_project] - - -if __name__ == '__main__': - sample_create_and_deploy_project() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_export_import_project.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_export_import_project.py deleted file mode 100644 index 9c04b300c61b..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_export_import_project.py +++ /dev/null @@ -1,83 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -""" -FILE: sample_export_import_project.py - -DESCRIPTION: - This sample demonstrates how to export and import a Qna project. - -USAGE: - python sample_export_import_project.py - - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. - 3) AZURE_QUESTIONANSWERING_PROJECT - your existing Question Answering project to export/import. -""" - - -def sample_export_import_project(): - # [START export_import_project] - import os - import io - import zipfile - from azure.core.rest import HttpRequest - from azure.core.credentials import AzureKeyCredential - from azure.ai.language.questionanswering.authoring import AuthoringClient - - # get service secrets - endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] - key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - project_name = os.environ["AZURE_QUESTIONANSWERING_PROJECT"] - - # create client - client = AuthoringClient(endpoint, AzureKeyCredential(key)) - with client: - - # export - export_format = "json" - export_poller = client.begin_export( - project_name=project_name, - file_format=export_format - ) - export_result = export_poller.result() - export_url = export_result["resultUrl"] - request = HttpRequest("GET", export_url) - exported_project = None - - if export_format == "json": - response = client.send_request(request) - exported_project = response.json() - elif export_format == "excel" or export_format == "tsv": - response = client.send_request(request, stream=True) - exported_project = zipfile.ZipFile(io.BytesIO(response.read())) - exported_project.extractall("./ExportedProject") - exported_project.close() - print(f"{export_format} project files written to ./ExportedProject.") - return - - # import project - import_poller = client.begin_import_assets( - project_name=f"{project_name}-imported", - options=exported_project, - ) - import_poller.result() - - # list projects - print("view all qna projects:") - qna_projects = client.list_projects() - for p in qna_projects: - if p["projectName"] == project_name: - print("project: {}".format(p["projectName"])) - print("\tlanguage: {}".format(p["language"])) - print("\tdescription: {}".format(p["description"])) - - # [END export_import_project] - - -if __name__ == '__main__': - sample_export_import_project() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_update_knowledge_sources.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_update_knowledge_sources.py deleted file mode 100644 index f60bab2cb017..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/authoring/sample_update_knowledge_sources.py +++ /dev/null @@ -1,125 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -""" -FILE: sample_update_knowledge_sources.py - -DESCRIPTION: - This sample demonstrates how to update Qna project knowledge sources. - -USAGE: - python sample_update_knowledge_sources.py - - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. -""" - -def sample_update_knowledge_sources(): - # [START update_knowledge_sources] - import os - from azure.core.credentials import AzureKeyCredential - from azure.ai.language.questionanswering.authoring import AuthoringClient - - # get service secrets - endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] - key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - - # create client - client = AuthoringClient(endpoint, AzureKeyCredential(key)) - with client: - - # create project - project_name = "Microsoft" - client.create_project( - project_name=project_name, - options={ - "description": "test project for some Microsoft QnAs", - "language": "en", - "multilingualResource": True, - "settings": { - "defaultAnswer": "no answer" - } - }) - - # sources - sources_poller = client.begin_update_sources( - project_name=project_name, - sources=[{ - "op": "add", - "value": { - "displayName": "MicrosoftFAQ", - "source": "https://www.microsoft.com/en-in/software-download/faq", - "sourceUri": "https://www.microsoft.com/en-in/software-download/faq", - "sourceKind": "url", - "contentStructureKind": "unstructured", - "refresh": False - } - }] - ) - sources = sources_poller.result() # wait until done - for item in sources: - print("source name: {}".format(item.get("displayName", "N/A"))) - print("\tsource: {}".format(item["source"])) - print("\tsource uri: {}".format(item.get("sourceUri", "N/A"))) - print("\tsource kind: {}".format(item["sourceKind"])) - - # qnas - qna_poller = client.begin_update_qnas( - project_name=project_name, - qnas=[{ - "op": "add", - "value": { - "questions": [ - "What is the easiest way to use azure services in my .NET project?" - ], - "answer": "Using Microsoft's Azure SDKs" - } - }] - ) - qnas = qna_poller.result() - for item in qnas: - print("qna: {}".format(item["id"])) - print("\tquestions:") - for question in item["questions"]: - print("\t\t{}".format(question)) - print("\tanswer: {}".format(item["answer"])) - - # synonyms - client.update_synonyms( - project_name=project_name, - synonyms={ - "value": [ - { - "alterations": [ - "qnamaker", - "qna maker" - ] - }, - { - "alterations": [ - "qna", - "question and answer" - ] - } - ] - } - ) - synonyms = client.list_synonyms( - project_name=project_name - ) - for item in synonyms: - print("synonyms:") - print("\talterations:") - for alt in item["alterations"]: - print("\t\t{}".format(alt)) - print('') - - # [END update_knowledge_sources] - - -if __name__ == '__main__': - sample_update_knowledge_sources() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_chat.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_chat.py index 225730665dac..12ee49d40450 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_chat.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_chat.py @@ -8,79 +8,95 @@ FILE: sample_chat.py DESCRIPTION: - This sample demonstrates how to ask a follow-up question (chit-chat) from a knowledge base. + Demonstrate a follow-up (contextual) question using `answer_context`. USAGE: python sample_chat.py - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. - 3) AZURE_QUESTIONANSWERING_PROJECT - the name of a knowledge base project. +Environment variables: + 1) AZURE_QUESTIONANSWERING_ENDPOINT + 2) AZURE_QUESTIONANSWERING_KEY + 3) AZURE_QUESTIONANSWERING_PROJECT + 4) AZURE_QUESTIONANSWERING_DEPLOYMENT (optional; defaults to 'production') """ +from __future__ import annotations + + def sample_chit_chat(): # [START chit_chat] import os from azure.core.credentials import AzureKeyCredential from azure.ai.language.questionanswering import QuestionAnsweringClient - from azure.ai.language.questionanswering import models as qna + from azure.ai.language.questionanswering.models import ( + AnswersOptions, + ShortAnswerOptions, + KnowledgeBaseAnswerContext, + ) endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - knowledge_base_project = os.environ["AZURE_QUESTIONANSWERING_PROJECT"] + project = os.environ["AZURE_QUESTIONANSWERING_PROJECT"] + deployment = os.environ.get("AZURE_QUESTIONANSWERING_DEPLOYMENT", "production") client = QuestionAnsweringClient(endpoint, AzureKeyCredential(key)) with client: - first_question="How long should my Surface battery last?" + first_question = "How long should my Surface battery last?" - output = client.get_answers( + first_options = AnswersOptions( question=first_question, top=3, confidence_threshold=0.2, include_unstructured_sources=True, - short_answer_options=qna.ShortAnswerOptions( - confidence_threshold=0.2, - top=1 - ), - project_name=knowledge_base_project, - deployment_name="test" + short_answer_options=ShortAnswerOptions(enable=True, confidence_threshold=0.2, top=1), + ) + first_output = client.get_answers( + first_options, + project_name=project, + deployment_name=deployment, + ) + best_candidate = next( + (a for a in (first_output.answers or []) if a.confidence and a.confidence > 0.7), + None, ) - if output.answers: - best_candidate = [a for a in output.answers if a.confidence and a.confidence > 0.7][0] - print(u"Q: {}".format(first_question)) - print(u"A: {}".format(best_candidate.answer)) - else: - print(f"No answers returned from question '{first_question}'") + if not best_candidate: + print(f"No high-confidence answers for '{first_question}'") return - if best_candidate.qna_id: - followup_question = "How long it takes to charge Surface?" + print(f"Q: {first_question}") + print(f"A: {best_candidate.answer}") - output = client.get_answers( + if best_candidate.qna_id: + followup_question = "How long does it take to charge a Surface?" + # Use answer_context to provide previous QnA selection for disambiguation. + followup_options = AnswersOptions( question=followup_question, top=3, confidence_threshold=0.2, - answer_context=qna.KnowledgeBaseAnswerContext( + answer_context=KnowledgeBaseAnswerContext( previous_question=first_question, - previous_qna_id=best_candidate.qna_id - ), - short_answer_options=qna.ShortAnswerOptions( - confidence_threshold=0.2, - top=1 + previous_qna_id=best_candidate.qna_id, ), + short_answer_options=ShortAnswerOptions(enable=True, confidence_threshold=0.2, top=1), include_unstructured_sources=True, - project_name=knowledge_base_project, - deployment_name="test" ) - if output.answers: - print(u"Q: {}".format(followup_question)) - print(u"A: {}".format(output.answers[0].answer)) + followup_output = client.get_answers( + followup_options, + project_name=project, + deployment_name=deployment, + ) + follow_best = next( + (a for a in (followup_output.answers or []) if a.confidence and a.confidence > 0.2), + None, + ) + if follow_best: + print(f"Q: {followup_question}") + print(f"A: {follow_best.answer}") else: - print(f"No answers returned from question '{followup_question}'") + print(f"No answers for follow-up '{followup_question}'") # [END chit_chat] -if __name__ == '__main__': - sample_chit_chat() \ No newline at end of file +if __name__ == "__main__": + sample_chit_chat() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_query_knowledgebase.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_query_knowledgebase.py index fd47b3ec54c7..965c797a7569 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_query_knowledgebase.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_query_knowledgebase.py @@ -8,15 +8,16 @@ FILE: sample_query_knowledgebase.py DESCRIPTION: - This sample demonstrates how to ask a question from a knowledge base. + Ask a question against a deployed knowledge base (flattened parameter style). USAGE: python sample_query_knowledgebase.py - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. - 3) AZURE_QUESTIONANSWERING_PROJECT - the name of a knowledge base project. +Environment variables: + 1) AZURE_QUESTIONANSWERING_ENDPOINT - endpoint for the resource + 2) AZURE_QUESTIONANSWERING_KEY - API key + 3) AZURE_QUESTIONANSWERING_PROJECT - knowledge base project name + 4) AZURE_QUESTIONANSWERING_DEPLOYMENT - (optional) deployment name (defaults to 'production') """ @@ -25,36 +26,42 @@ def sample_query_knowledgebase(): import os from azure.core.credentials import AzureKeyCredential from azure.ai.language.questionanswering import QuestionAnsweringClient - from azure.ai.language.questionanswering import models as qna + from azure.ai.language.questionanswering.models import AnswersOptions, ShortAnswerOptions endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] key = os.environ["AZURE_QUESTIONANSWERING_KEY"] - knowledge_base_project = os.environ["AZURE_QUESTIONANSWERING_PROJECT"] + project = os.environ["AZURE_QUESTIONANSWERING_PROJECT"] + deployment = os.environ.get("AZURE_QUESTIONANSWERING_DEPLOYMENT", "production") client = QuestionAnsweringClient(endpoint, AzureKeyCredential(key)) with client: - question="How long should my Surface battery last?" - output = client.get_answers( + question = "How long should my Surface battery last?" + + options = AnswersOptions( question=question, top=3, confidence_threshold=0.2, include_unstructured_sources=True, - short_answer_options=qna.ShortAnswerOptions( - confidence_threshold=0.2, - top=1 - ), - project_name=knowledge_base_project, - deployment_name="test" + short_answer_options=ShortAnswerOptions(enable=True, confidence_threshold=0.2, top=1), + ) + output = client.get_answers( + options, + project_name=project, + deployment_name=deployment, + ) + + best_candidate = next( + (a for a in (output.answers or []) if a.confidence and a.confidence > 0.7), + None, ) - if output.answers: - best_candidate = [a for a in output.answers if a.confidence and a.confidence > 0.7][0] - print("Q: {}".format(question)) - print("A: {}".format(best_candidate.answer)) + if best_candidate: + print(f"Q: {question}") + print(f"A: {best_candidate.answer}") else: - print(f"No answers returned from question '{question}'") + print(f"No answers for '{question}'") # [END query_knowledgebase] -if __name__ == '__main__': +if __name__ == "__main__": sample_query_knowledgebase() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_query_text.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_query_text.py index 4f61f67bbbc6..2efde049cbe3 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_query_text.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples/sample_query_text.py @@ -8,50 +8,69 @@ FILE: sample_query_text.py DESCRIPTION: - This sample demonstrates how to ask a question from supplied text data. + Ask a question over ad-hoc text (options object pattern). USAGE: python sample_query_text.py - Set the environment variables with your own values before running the sample: - 1) AZURE_QUESTIONANSWERING_ENDPOINT - the endpoint to your QuestionAnswering resource. - 2) AZURE_QUESTIONANSWERING_KEY - your QuestionAnswering API key. +Environment variables: + 1) AZURE_QUESTIONANSWERING_ENDPOINT + 2) AZURE_QUESTIONANSWERING_KEY """ +from __future__ import annotations + def sample_query_text(): # [START query_text] import os from azure.core.credentials import AzureKeyCredential from azure.ai.language.questionanswering import QuestionAnsweringClient - from azure.ai.language.questionanswering import models as qna + from azure.ai.language.questionanswering.models import AnswersFromTextOptions, TextDocument endpoint = os.environ["AZURE_QUESTIONANSWERING_ENDPOINT"] key = os.environ["AZURE_QUESTIONANSWERING_KEY"] client = QuestionAnsweringClient(endpoint, AzureKeyCredential(key)) with client: - question="How long it takes to charge surface?" - input = qna.AnswersFromTextOptions( + question = "How long does it take to charge a Surface?" + + # Options object call + options = AnswersFromTextOptions( question=question, text_documents=[ - "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you're using your Surface for power-intensive activities like gaming or video streaming while you're charging it.", - "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. " + - "The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface.", - ] + TextDocument( + id="doc1", + text=( + "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully " + "from an empty state. It can take longer if you're using your Surface for power-intensive " + "activities like gaming or video streaming while you're charging it." + ), + ), + TextDocument( + id="doc2", + text=( + "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like " + "a phone, while your Surface charges. The USB port on the power supply is only for charging, " + "not for data transfer." + ), + ), + ], ) - output = client.get_answers_from_text(input) - if output.answers: - best_answer = [a for a in output.answers if a.confidence and a.confidence > 0.9][0] - print("Q: {}".format(question)) - print("A: {}".format(best_answer.answer)) + output = client.get_answers_from_text(options) + best_answer = next( + (a for a in (output.answers or []) if a.confidence and a.confidence > 0.9), + None, + ) + if best_answer: + print(f"Q: {question}") + print(f"A: {best_answer.answer}") else: - print(f"No answers returned from question '{question}'") + print(f"No answers for '{question}'") # [END query_text] -if __name__ == '__main__': +if __name__ == "__main__": sample_query_text() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/setup.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/setup.py deleted file mode 100644 index 4e14c96fc277..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/setup.py +++ /dev/null @@ -1,76 +0,0 @@ -from setuptools import setup, find_packages -import os -from io import open -import re - -PACKAGE_NAME = "azure-ai-language-questionanswering" -PACKAGE_PPRINT_NAME = "Question Answering" - -# a-b-c => a/b/c -package_folder_path = PACKAGE_NAME.replace('-', '/') -# a-b-c => a.b.c -namespace_name = PACKAGE_NAME.replace('-', '.') - -# Version extraction inspired from 'requests' -with open(os.path.join(package_folder_path, '_version.py'), 'r') as fd: - version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', - fd.read(), re.MULTILINE).group(1) -if not version: - raise RuntimeError('Cannot find version information') - -with open('README.md', encoding='utf-8') as f: - long_description = f.read() - -setup( - name=PACKAGE_NAME, - version=version, - description='Microsoft Azure {} Client Library for Python'.format(PACKAGE_PPRINT_NAME), - - # ensure that these are updated to reflect the package owners' information - long_description=long_description, - long_description_content_type='text/markdown', - url='https://github.com/Azure/azure-sdk-for-python', - keywords="azure, azure sdk", - author='Microsoft Corporation', - author_email='azuresdkengsysadmins@microsoft.com', - - license='MIT License', - # ensure that the development status reflects the status of your package - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Programming Language :: Python", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "License :: OSI Approved :: MIT License", - ], - python_requires=">=3.8", - zip_safe=False, - packages=find_packages(exclude=[ - 'tests', - # Exclude packages that will be covered by PEP420 or nspkg - # This means any folder structure that only consists of a __init__.py. - # For example, for storage, this would mean adding 'azure.storage' - # in addition to the default 'azure' that is seen here. - 'azure', - 'azure.ai', - 'azure.ai.language', - ]), - include_package_data=True, - package_data={ - 'azure.ai.language.questionanswering': ['py.typed'], - }, - install_requires=[ - "azure-core>=1.28.0", - "isodate>=0.6.1", - "typing-extensions>=4.0.1", - ], - project_urls={ - 'Bug Reports': 'https://github.com/Azure/azure-sdk-for-python/issues', - 'Source': 'https://github.com/Azure/azure-sdk-for-python', - } -) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/swagger/README.md b/sdk/cognitivelanguage/azure-ai-language-questionanswering/swagger/README.md deleted file mode 100644 index 32d52efd8fa6..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/swagger/README.md +++ /dev/null @@ -1,358 +0,0 @@ -# Azure QnA for Python - -> see https://aka.ms/autorest - -### Setup - -Install Autorest v3 - -```ps -npm install -g autorest -``` - -### Generation - -```ps -cd -autorest -``` - -1) After generation, run the [postprocessing](https://github.com/Azure/autorest.python/blob/autorestv3/docs/customizations.md#postprocessing) script to fix linting issues in the runtime library. - -`autorest --postprocess --output-folder= --perform-load=false --python` - -### Settings - -```yaml -namespace: azure.ai.language.questionanswering -package-name: azure-ai-language-questionanswering -license-header: MICROSOFT_MIT_NO_VERSION -clear-output-folder: true -no-namespace-folders: true -python: true -version-tolerant: true -package-version: 1.1.1 -add-credential: true -credential-default-policy-type: AzureKeyCredentialPolicy -credential-key-header-name: Ocp-Apim-Subscription-Key -black: true -``` - -## Batch Execution - -```yaml -batch: - - tag: release_runtime_1_1 - - tag: release_authoring_1_1 -``` - - -## Runtime - -These settings apply only when `--tag=release_runtime_1_1` is specified on the command line. - -```yaml $(tag) == 'release_runtime_1_1' -input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/59ad2b7dd63e952822aa51e11a26a0af5724f996/specification/cognitiveservices/data-plane/Language/stable/2021-10-01/questionanswering.json -output-folder: ../azure/ai/language/questionanswering -models-mode: msrest -title: QuestionAnsweringClient -``` - -## Authoring - -These settings apply only when `--tag=release_authoring_1_1` is specified on the command line. - -```yaml $(tag) == 'release_authoring_1_1' -input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/59ad2b7dd63e952822aa51e11a26a0af5724f996/specification/cognitiveservices/data-plane/Language/stable/2021-10-01/questionanswering-authoring.json -output-folder: ../azure/ai/language/questionanswering/authoring -title: AuthoringClient -``` - - - -## Customizations - -### General customizations - -#### Add docs to authoring operations - -```yaml -directive: -- from: questionanswering-authoring.json - where: $.paths.*.* - transform: | - var operationId = $.operationId.replace(/_/g, "/").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); - $.description = "See https://learn.microsoft.com/rest/api/cognitiveservices/questionanswering/" + operationId + " for more information."; - -# Fix too long of link in description. -- from: swagger-document - where: $.info - transform: | - $["description"] = "The language service API is a suite of natural language processing (NLP) skills built with best-in-class Microsoft machine learning algorithms. The API can be used to analyze unstructured text for tasks such as sentiment analysis, key phrase extraction, language detection and question answering. Further documentation can be found in https://learn.microsoft.com/azure/cognitive-services/text-analytics/overview"; -``` - -```yaml -# Define HTTP 200 responses for LROs to document result model. -# Note there is no transform for DeleteProject. This should return None. -directive: -- where-operation: QuestionAnsweringProjects_DeployProject - transform: | - $.responses["200"] = { - description: "Project deployment details.", - schema: { - "$ref": "#/definitions/ProjectDeployment" - } - }; -- where-operation: QuestionAnsweringProjects_Import - transform: | - $.responses["200"] = { - description: "Gets the status of an Import job.", - schema: { - "$ref": "#/definitions/JobState" - } - }; -- where-operation: QuestionAnsweringProjects_UpdateQnas - transform: | - $["x-ms-pageable"] = { - "nextLinkName": "nextLink", - "itemName": "value" - }; - $.responses["200"] = { - description: "All the QnAs of a project.", - schema: { - "$ref": "#/definitions/QnaAssets" - } - }; -- where-operation: QuestionAnsweringProjects_UpdateSources - transform: | - $["x-ms-pageable"] = { - "nextLinkName": "nextLink", - "itemName": "value" - }; - $.responses["200"] = { - description: "All the sources of a project.", - schema: { - "$ref": "#/definitions/QnaSources" - } - }; -``` - - -### Python Customizations - -### Runtime - - -#### Rename "QuestionAnsweringKnowledgeBase_Query" -> "GetAnswers" - -```yaml -directive: - - from: swagger-document - where: $["paths"]["/:query-knowledgebases"]["post"] - transform: > - $["operationId"] = "getAnswers"; -``` - -#### Rename "QuestionAnsweringText_Query" -> "GetAnswersFromText" - -```yaml -directive: - - from: swagger-document - where: $["paths"]["/:query-text"]["post"] - transform: > - $["operationId"] = "getAnswersFromText"; -``` - -#### Rename `KnowledgeBasedQueryOptions` -> `Options` - -```yaml -directive: - - from: swagger-document - where: $["parameters"]["AnswersOptions"] - transform: > - $["x-ms-client-name"] = "Options"; -``` - -#### Rename `TextQueryOptions` -> `Options` - -```yaml -directive: - - from: swagger-document - where: $["parameters"]["AnswersFromTextOptions"] - transform: > - $["x-ms-client-name"] = "Options"; -``` - -#### Delete `StringIndexType` - -```yaml -directive: - - from: swagger-document - where: $["definitions"]["AnswersFromTextOptions"] - transform: > - delete $.properties["stringIndexType"] -``` - -#### Delete `RankerKind` and `LogicalOperationKind` enums - -```yaml -directive: - - from: swagger-document - where: $["definitions"] - transform: > - delete $["AnswersOptions"]["properties"]["rankerType"]["x-ms-enum"]; - delete $["AnswersOptions"]["properties"]["rankerType"]["enum"]; - delete $["LogicalOperationKind"]["x-ms-enum"]; - delete $["LogicalOperationKind"]["enum"]; -``` - -#### Make `MetadataFilter`'s `metadata` property a list of string - -```yaml -directive: - - from: swagger-document - where: $["definitions"] - transform: > - delete $["MetadataFilter"]["properties"]["metadata"]["items"]["$ref"]; - $["MetadataFilter"]["properties"]["metadata"]["items"]["type"] = "object"; - delete $["MetadataRecord"]; -``` - -### Authoring - - -#### Remove operation group - -```yaml -directive: - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects"]["get"] - transform: > - $["operationId"] = "listProjects"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}"]["get"] - transform: > - $["operationId"] = "getProjectDetails"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}"]["patch"] - transform: > - $["operationId"] = "createProject"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}"]["delete"] - transform: > - $["operationId"] = "deleteProject"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/:export"]["post"] - transform: > - $["operationId"] = "export"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/:import"]["post"] - transform: > - $["operationId"] = "importAssets"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/deployments/{deploymentName}"]["put"] - transform: > - $["operationId"] = "deployProject"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/deployments"]["get"] - transform: > - $["operationId"] = "listDeployments"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/synonyms"]["get"] - transform: > - $["operationId"] = "listSynonyms"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/synonyms"]["put"] - transform: > - $["operationId"] = "updateSynonyms"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/sources"]["get"] - transform: > - $["operationId"] = "listSources"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/sources"]["patch"] - transform: > - $["operationId"] = "updateSources"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/qnas"]["get"] - transform: > - $["operationId"] = "listQnas"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/qnas"]["patch"] - transform: > - $["operationId"] = "updateQnas"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/feedback"]["post"] - transform: > - $["operationId"] = "addFeedback"; -``` - -#### Remove status operations - -```yaml -directive: - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/deletion-jobs/{jobId}"] - transform: > - delete $["get"]; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/export/jobs/{jobId}"] - transform: > - delete $["get"]; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/import/jobs/{jobId}"] - transform: > - delete $["get"]; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/deployments/{deploymentName}/jobs/{jobId}"] - transform: > - delete $["get"]; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/sources/jobs/{jobId}"] - transform: > - delete $["get"]; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/qnas/jobs/{jobId}"] - transform: > - delete $["get"]; -``` - -#### Rename body parameter - -```yaml -directive: - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/feedback"]["post"] - transform: > - $["parameters"][2]["x-ms-client-name"] = "feedback"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/qnas"]["patch"] - transform: > - $["parameters"][2]["x-ms-client-name"] = "qnas"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/sources"]["patch"] - transform: > - $["parameters"][2]["x-ms-client-name"] = "sources"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/synonyms"]["put"] - transform: > - $["parameters"][2]["x-ms-client-name"] = "synonyms"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}/:import"]["post"] - transform: > - $["parameters"][2]["x-ms-client-name"] = "options"; - - from: swagger-document - where: $["paths"]["/query-knowledgebases/projects/{projectName}"]["patch"] - transform: > - $["parameters"][1]["x-ms-client-name"] = "options"; -``` - -#### Rename format parameter - -```yaml -directive: - - from: swagger-document - where: $["parameters"]["ImportExportFormatParameter"] - transform: > - $["x-ms-client-name"] = "file_format"; -``` diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/conftest.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/conftest.py index e90cad1470b5..68d3d083b6cd 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/conftest.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/conftest.py @@ -1,52 +1,44 @@ # coding=utf-8 # ------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -------------------------------------------------------------------------- +# Runtime test configuration for Question Answering (authoring removed) +# ------------------------------------------------------------------------- import pytest from devtools_testutils.sanitizers import ( add_header_regex_sanitizer, add_oauth_response_sanitizer, - remove_batch_sanitizers + remove_batch_sanitizers, ) - # Environment variable keys ENV_ENDPOINT = "AZURE_QUESTIONANSWERING_ENDPOINT" ENV_KEY = "AZURE_QUESTIONANSWERING_KEY" ENV_PROJECT = "AZURE_QUESTIONANSWERING_PROJECT" -ENV_SUBSCRIPTION_ID = "AZURE_SUBSCRIPTION_ID" -ENV_TENANT_ID = "AZURE_TENANT_ID" -ENV_CLIENT_ID = "AZURE_CLIENT_ID" -ENV_CLIENT_SECRET = "AZURE_CLIENT_SECRET" -# Fake values -TEST_ENDPOINT = "https://test-resource.api.cognitive.microsoft.com/" +# Fake values for playback +TEST_ENDPOINT = "https://test-resource.cognitiveservices.azure.com/" TEST_KEY = "0000000000000000" TEST_PROJECT = "test-project" -TEST_ID = "00000000-0000-0000-0000-000000000000" @pytest.fixture(scope="session", autouse=True) -def add_sanitizers(test_proxy, environment_variables): +def add_sanitizers(test_proxy, environment_variables): # pylint: disable=unused-argument + """Configure sanitization for recordings. + + We intentionally keep project name visible for routing but sanitize endpoint & key. + Removed subscription/tenant/client credentials since authoring/AAD management tests are not here. + """ sanitization_mapping = { ENV_ENDPOINT: TEST_ENDPOINT, ENV_KEY: TEST_KEY, ENV_PROJECT: TEST_PROJECT, - ENV_SUBSCRIPTION_ID: TEST_ID, - ENV_TENANT_ID: TEST_ID, - ENV_CLIENT_ID: TEST_ID, - ENV_CLIENT_SECRET: TEST_ID } environment_variables.sanitize_batch(sanitization_mapping) add_oauth_response_sanitizer() add_header_regex_sanitizer(key="Set-Cookie", value="[set-cookie;]") - # Remove the following sanitizers since certain fields are needed in tests and are non-sensitive: - # - AZSDK3430: $..id - remove_batch_sanitizers(["AZSDK3430"]) + # Keep id fields (previously removed by AZSDK3430) since assertions validate them. + remove_batch_sanitizers(["AZSDK3430"]) # ensure it's not applied @pytest.fixture(scope="session") @@ -54,5 +46,5 @@ def qna_creds(environment_variables): yield { "qna_endpoint": environment_variables.get(ENV_ENDPOINT), "qna_key": environment_variables.get(ENV_KEY), - "qna_project": environment_variables.get(ENV_PROJECT) + "qna_project": environment_variables.get(ENV_PROJECT), } diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/helpers.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/helpers.py deleted file mode 100644 index cc14e7d32549..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/helpers.py +++ /dev/null @@ -1,139 +0,0 @@ -# coding=utf-8 -# ------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -------------------------------------------------------------------------- - -class QnaAuthoringHelper: - - def create_test_project( - client, - project_name = "IssacNewton", - is_deployable = False, - add_sources = False, - get_export_url = False, - delete_old_project = False, - add_qnas = False, - **kwargs - ): - # create project - client.create_project( - project_name=project_name, - options={ - "description": "biography of Sir Issac Newton", - "language": "en", - "multilingualResource": True, - "settings": { - "defaultAnswer": "no answer" - } - }) - - # add sources - if is_deployable or add_sources: - QnaAuthoringHelper.add_sources(client, project_name, **kwargs) - - if get_export_url: - return QnaAuthoringHelper.export_project(client, project_name, delete_project=delete_old_project, **kwargs) - - def add_sources(client, project_name, **kwargs): - update_sources_poller = client.begin_update_sources( - project_name=project_name, - sources=[ - { - "op": "add", - "value": { - "displayName": "Issac Newton Bio", - "sourceUri": "https://wikipedia.org/wiki/Isaac_Newton", - "sourceKind": "url" - } - } - ], - **kwargs - ) - update_sources_poller.result() - - def export_project(client, project_name, delete_project=True, **kwargs): - # export project - export_poller = client.begin_export( - project_name=project_name, - file_format="json", - **kwargs - ) - result = export_poller.result() - - # delete old project - if delete_project: - delete_poller = client.begin_delete_project( - project_name=project_name, - **kwargs - ) - delete_poller.result() - return result["resultUrl"] - - -class QnaAuthoringAsyncHelper: - - async def create_test_project( - client, - project_name = "IssacNewton", - is_deployable = False, - add_sources = False, - get_export_url = False, - delete_old_project = False, - add_qnas = False, - **kwargs - ): - # create project - await client.create_project( - project_name=project_name, - options={ - "description": "biography of Sir Issac Newton", - "language": "en", - "multilingualResource": True, - "settings": { - "defaultAnswer": "no answer" - } - }) - - # add sources - if is_deployable or add_sources: - await QnaAuthoringAsyncHelper.add_sources(client, project_name, **kwargs) - - if get_export_url: - return await QnaAuthoringAsyncHelper.export_project(client, project_name, delete_project=delete_old_project, **kwargs) - - async def add_sources(client, project_name, **kwargs): - update_sources_poller = await client.begin_update_sources( - project_name=project_name, - sources=[ - { - "op": "add", - "value": { - "displayName": "Issac Newton Bio", - "sourceUri": "https://wikipedia.org/wiki/Isaac_Newton", - "sourceKind": "url" - } - } - ], - **kwargs - ) - await update_sources_poller.result() - - async def export_project(client, project_name, delete_project=True, **kwargs): - # export project - export_poller = await client.begin_export( - project_name=project_name, - file_format="json", - **kwargs - ) - result = await export_poller.result() - - # delete old project - if delete_project: - delete_poller = await client.begin_delete_project( - project_name=project_name, - **kwargs - ) - await delete_poller.result() - return result["resultUrl"] diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_create_and_deploy_project.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_create_and_deploy_project.py deleted file mode 100644 index f72e61f7e3b4..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_create_and_deploy_project.py +++ /dev/null @@ -1,105 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest - -from azure.ai.language.questionanswering.authoring import AuthoringClient -from azure.core.credentials import AzureKeyCredential - -from helpers import QnaAuthoringHelper -from testcase import QuestionAnsweringTestCase - - -class TestCreateAndDeploy(QuestionAnsweringTestCase): - - def test_polling_interval(self, qna_creds): - # test default - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - assert client._config.polling_interval == 5 - # test override - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"]), polling_interval=1) - assert client._config.polling_interval == 1 - - def test_create_project_aad(self, recorded_test, qna_creds): - token = self.get_credential(AuthoringClient) - client = AuthoringClient(qna_creds["qna_endpoint"], token) - - # create project - project_name = "IssacNewton" - client.create_project( - project_name=project_name, - options={ - "description": "biography of Sir Issac Newton", - "language": "en", - "multilingualResource": True, - "settings": { - "defaultAnswer": "no answer" - } - }) - - # list projects - qna_projects = client.list_projects() - found = False - for p in qna_projects: - if ("projectName" in p) and p["projectName"] == project_name: - found = True - assert found - - def test_create_project(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - client.create_project( - project_name=project_name, - options={ - "description": "biography of Sir Issac Newton", - "language": "en", - "multilingualResource": True, - "settings": { - "defaultAnswer": "no answer" - } - }) - - # list projects - qna_projects = client.list_projects() - found = False - for p in qna_projects: - if ("projectName" in p) and p["projectName"] == project_name: - found = True - assert found - - def test_deploy_project(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create deployable project - project_name = "IssacNewton" - QnaAuthoringHelper.create_test_project( - client, - project_name=project_name, - is_deployable=True, - **self.kwargs_for_polling, - ) - - # test deploy - deployment_name = "production" - deployment_poller = client.begin_deploy_project( - project_name=project_name, - deployment_name=deployment_name, - **self.kwargs_for_polling - ) - project = deployment_poller.result() - assert project["lastDeployedDateTime"] - assert project["deploymentName"] == "production" - - # assert - deployments = client.list_deployments( - project_name=project_name - ) - deployment_found = False - for d in deployments: - if ("deploymentName" in d) and d["deploymentName"] == deployment_name: - deployment_found = True - assert deployment_found diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_create_and_deploy_project_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_create_and_deploy_project_async.py deleted file mode 100644 index f916f9d5262b..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_create_and_deploy_project_async.py +++ /dev/null @@ -1,108 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest - -from azure.ai.language.questionanswering.authoring.aio import AuthoringClient -from azure.core.credentials import AzureKeyCredential - -from helpers import QnaAuthoringAsyncHelper -from testcase import QuestionAnsweringTestCase - - -class TestCreateAndDeployAsync(QuestionAnsweringTestCase): - - def test_polling_interval(self, qna_creds): - # test default - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - assert client._config.polling_interval == 5 - # test override - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"]), polling_interval=1) - assert client._config.polling_interval == 1 - - @pytest.mark.asyncio - async def test_create_project_aad(self, recorded_test, qna_creds): - token = self.get_credential(AuthoringClient, is_async=True) - client = AuthoringClient(qna_creds["qna_endpoint"], token) - - # create project - project_name = "IssacNewton" - await client.create_project( - project_name=project_name, - options={ - "description": "biography of Sir Issac Newton", - "language": "en", - "multilingualResource": True, - "settings": { - "defaultAnswer": "no answer" - } - }) - - # list projects - qna_projects = client.list_projects() - found = False - async for p in qna_projects: - if ("projectName" in p) and p["projectName"] == project_name: - found = True - assert found - - @pytest.mark.asyncio - async def test_create_project(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - await client.create_project( - project_name=project_name, - options={ - "description": "biography of Sir Issac Newton", - "language": "en", - "multilingualResource": True, - "settings": { - "defaultAnswer": "no answer" - } - }) - - # list projects - qna_projects = client.list_projects() - found = False - async for p in qna_projects: - if ("projectName" in p) and p["projectName"] == project_name: - found = True - assert found - - @pytest.mark.asyncio - async def test_deploy_project(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create deployable project - project_name = "IssacNewton" - await QnaAuthoringAsyncHelper.create_test_project( - client, - project_name=project_name, - is_deployable=True, - **self.kwargs_for_polling - ) - - # test deploy - deployment_name = "production" - deployment_poller = await client.begin_deploy_project( - project_name=project_name, - deployment_name=deployment_name, - **self.kwargs_for_polling - ) - project = await deployment_poller.result() - assert project["lastDeployedDateTime"] - assert project["deploymentName"] == "production" - - # assert - deployments = client.list_deployments( - project_name=project_name - ) - deployment_found = False - async for d in deployments: - if ("deploymentName" in d) and d["deploymentName"] == deployment_name: - deployment_found = True - assert deployment_found diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_export_import_project.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_export_import_project.py deleted file mode 100644 index 26461631647e..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_export_import_project.py +++ /dev/null @@ -1,71 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -from azure.ai.language.questionanswering.authoring import AuthoringClient -from azure.core.credentials import AzureKeyCredential - -from helpers import QnaAuthoringHelper -from testcase import QuestionAnsweringTestCase - - -class TestExportAndImport(QuestionAnsweringTestCase): - - def test_export_project(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - QnaAuthoringHelper.create_test_project(client, project_name=project_name, **self.kwargs_for_polling) - - # export project - export_poller = client.begin_export( - project_name=project_name, - file_format="json", - **self.kwargs_for_polling - ) - result = export_poller.result() - assert result["status"] == "succeeded" - assert result["resultUrl"] is not None - - def test_import_project(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - export_url = QnaAuthoringHelper.create_test_project( - client, - project_name=project_name, - get_export_url=True, - delete_old_project=True, - **self.kwargs_for_polling - ) - - # import project - project = { - "Metadata": { - "ProjectName": project_name, - "Description": "biography of Sir Issac Newton", - "Language": "en", - "MultilingualResource": False, - "Settings": { - "DefaultAnswer": "no answer" - } - } - } - import_poller = client.begin_import_assets( - project_name=project_name, - options=project, - **self.kwargs_for_polling - ) - job_state = import_poller.result() - assert job_state["jobId"] - - # assert - project_found = False - projects = client.list_projects() - for p in projects: - if ("projectName" in p) and p["projectName"] == project_name: - project_found = True - assert project_found diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_export_import_project_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_export_import_project_async.py deleted file mode 100644 index faad628f6bdd..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_export_import_project_async.py +++ /dev/null @@ -1,75 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest - -from azure.ai.language.questionanswering.authoring.aio import AuthoringClient -from azure.core.credentials import AzureKeyCredential - -from helpers import QnaAuthoringAsyncHelper -from testcase import QuestionAnsweringTestCase - - -class TestExportAndImportAsync(QuestionAnsweringTestCase): - - @pytest.mark.asyncio - async def test_export_project(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - await QnaAuthoringAsyncHelper.create_test_project(client, project_name=project_name, **self.kwargs_for_polling) - - # export project - export_poller = await client.begin_export( - project_name=project_name, - file_format="json", - **self.kwargs_for_polling - ) - result = await export_poller.result() - assert result["status"] == "succeeded" - assert result["resultUrl"] is not None - - @pytest.mark.asyncio - async def test_import_project(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - export_url = await QnaAuthoringAsyncHelper.create_test_project( - client, - project_name=project_name, - get_export_url=True, - delete_old_project=True, - **self.kwargs_for_polling - ) - - # import project - project = { - "Metadata": { - "ProjectName": project_name, - "Description": "biography of Sir Issac Newton", - "Language": "en", - "MultilingualResource": False, - "Settings": { - "DefaultAnswer": "no answer" - } - } - } - import_poller = await client.begin_import_assets( - project_name=project_name, - options=project, - **self.kwargs_for_polling - ) - job_state = await import_poller.result() - assert job_state["jobId"] - - # assert - project_found = False - projects = client.list_projects() - async for p in projects: - if ("projectName" in p) and p["projectName"] == project_name: - project_found = True - assert project_found diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_knowledgebase.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_knowledgebase.py index 0c8d68c2fef6..6ac76a9a16d7 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_knowledgebase.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_knowledgebase.py @@ -1,18 +1,17 @@ +# pylint: disable=line-too-long,useless-suppression # coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ +# ------------------------------------------------------------------------- +# Runtime tests: knowledge base querying (authoring removed) +# ------------------------------------------------------------------------- import pytest from azure.ai.language.questionanswering import QuestionAnsweringClient -from azure.ai.language.questionanswering._operations._operations import \ - build_question_answering_get_answers_request as build_get_answers_request from azure.ai.language.questionanswering.models import ( AnswersOptions, KnowledgeBaseAnswerContext, ShortAnswerOptions, MetadataFilter, + MetadataRecord, QueryFilters, ) from azure.core.credentials import AzureKeyCredential @@ -21,450 +20,86 @@ class TestQnAKnowledgeBase(QuestionAnsweringTestCase): - - def test_query_knowledgebase_llc(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - json_content = { - "question": "Ports and connectors", - "top": 3, - "context": { - "previousUserQuery": "Meet Surface Pro 4", - "previousQnAId": 4 - } - } - request = build_get_answers_request( - json=json_content, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - with client: - response = client.send_request(request) - assert response.status_code == 200 - - output = response.json() - assert output - assert output.get('answers') - for answer in output['answers']: - assert answer.get('answer') - assert answer.get('confidenceScore') - assert answer.get('id') - assert answer.get('source') - assert answer.get('metadata') is not None - assert not answer.get('answerSpan') - - assert answer.get('questions') - for question in answer['questions']: - assert question - - assert answer.get('dialog') - assert answer['dialog'].get('isContextOnly') is not None - assert answer['dialog'].get('prompts') is not None - if answer['dialog'].get('prompts'): - for prompt in answer['dialog']['prompts']: - assert prompt.get('displayOrder') is not None - assert prompt.get('qnaId') - assert prompt.get('displayText') - - def test_query_knowledgebase_llc_with_answerspan(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - json_content = { - "question": "Ports and connectors", - "top": 3, - "context": { - "previousUserQuery": "Meet Surface Pro 4", - "previousQnAId": 4 - }, - "answerSpanRequest": { - "enable": True, - "confidenceScoreThreshold": 0.1, - "topAnswersWithSpan": 1 - } - } - request = build_get_answers_request( - json=json_content, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - with client: - response = client.send_request(request) - assert response.status_code == 200 - - output = response.json() - assert output - assert output.get('answers') - for answer in output['answers']: - assert answer.get('answer') - assert answer.get('confidenceScore') - assert answer.get('id') - assert answer.get('source') - assert answer.get('metadata') is not None - - if answer.get('answerSpan'): - assert answer['answerSpan'].get('text') - assert answer['answerSpan'].get('confidenceScore') - assert answer['answerSpan'].get('offset') is not None - assert answer['answerSpan'].get('length') - - assert answer.get('questions') - for question in answer['questions']: - assert question - - assert answer.get('dialog') - assert answer['dialog'].get('isContextOnly') is not None - assert answer['dialog'].get('prompts') is not None - if answer['dialog'].get('prompts'): - for prompt in answer['dialog']['prompts']: - assert prompt.get('displayOrder') is not None - assert prompt.get('qnaId') - assert prompt.get('displayText') - - def test_query_knowledgebase(self, recorded_test, qna_creds): + def test_query_knowledgebase(self, recorded_test, qna_creds): # standard model usage client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) query_params = AnswersOptions( question="Ports and connectors", top=3, - answer_context=KnowledgeBaseAnswerContext( - previous_question="Meet Surface Pro 4", - previous_qna_id=4 - ) + answer_context=KnowledgeBaseAnswerContext(previous_question="Meet Surface Pro 4", previous_qna_id=4), ) - with client: - output = client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - + output = client.get_answers(query_params, project_name=qna_creds["qna_project"], deployment_name="production") assert output.answers for answer in output.answers: assert answer.answer - assert answer.confidence - assert answer.qna_id + assert answer.confidence is not None + assert answer.qna_id is not None assert answer.source - assert answer.metadata is not None - assert not answer.short_answer - - assert answer.questions - for question in answer.questions: - assert question - - assert answer.dialog - assert answer.dialog.is_context_only is not None - assert answer.dialog.prompts is not None - if answer.dialog.prompts: - for prompt in answer.dialog.prompts: - assert prompt.display_order is not None - assert prompt.qna_id - assert prompt.display_text - - def test_query_knowledgebase_aad(self, recorded_test, qna_creds): - token = self.get_credential(QuestionAnsweringClient) - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], token) - query_params = AnswersOptions( - question="Ports and connectors", - top=3, - answer_context=KnowledgeBaseAnswerContext( - previous_question="Meet Surface Pro 4", - previous_qna_id=4 - ) - ) - - with client: - output = client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - - assert output.answers - for answer in output.answers: - assert answer.answer - assert answer.confidence - assert answer.qna_id - assert answer.source - assert answer.metadata is not None - assert not answer.short_answer - - assert answer.questions - for question in answer.questions: - assert question - - assert answer.dialog - assert answer.dialog.is_context_only is not None - assert answer.dialog.prompts is not None - if answer.dialog.prompts: - for prompt in answer.dialog.prompts: - assert prompt.display_order is not None - assert prompt.qna_id - assert prompt.display_text def test_query_knowledgebase_with_answerspan(self, recorded_test, qna_creds): client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) query_params = AnswersOptions( question="Ports and connectors", top=3, - answer_context=KnowledgeBaseAnswerContext( - previous_question="Meet Surface Pro 4", - previous_qna_id=4 - ), - short_answer_options=ShortAnswerOptions( - confidence_threshold=0.1, - top=2 - ) + answer_context=KnowledgeBaseAnswerContext(previous_question="Meet Surface Pro 4", previous_qna_id=4), + short_answer_options=ShortAnswerOptions(enable=True, confidence_threshold=0.1, top=2), ) - with client: - output = client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - + output = client.get_answers(query_params, project_name=qna_creds["qna_project"], deployment_name="test") assert output.answers + # If short answer returned, essential fields exist for answer in output.answers: - assert answer.answer - assert answer.confidence - assert answer.qna_id - assert answer.source - assert answer.metadata is not None - if answer.short_answer: assert answer.short_answer.text - assert answer.short_answer.confidence - assert answer.short_answer.offset is not None - assert answer.short_answer.length - - assert answer.questions - for question in answer.questions: - assert question - - assert answer.dialog - assert answer.dialog.is_context_only is not None - assert answer.dialog.prompts is not None - if answer.dialog.prompts: - for prompt in answer.dialog.prompts: - assert prompt.display_order is not None - assert prompt.qna_id - assert prompt.display_text - - @pytest.mark.live_test_only("Needs re-recording to work with new common sanitizers") - def test_query_knowledgebase_with_dictparams(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - query_params = { - "question": "How long should my Surface battery last?", - "top": 3, - "userId": "sd53lsY=", - "confidenceScoreThreshold": 0.2, - "answerSpanRequest": { - "enable": True, - "confidenceScoreThreshold": 0.2, - "topAnswersWithSpan": 1 - }, - "includeUnstructuredSources": True - } - - with client: - output = client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - - assert len(output.answers) == 3 - confident_answers = [a for a in output.answers if a.confidence > 0.7] - assert len(confident_answers) == 1 - assert confident_answers[0].source == "surface-book-user-guide-EN.pdf" - - @pytest.mark.live_test_only("Needs re-recording to work with new common sanitizers") - def test_query_knowledgebase_overload(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - with client: - output = client.get_answers( - project_name=qna_creds["qna_project"], - deployment_name='test', - question="How long should my Surface battery last?", - top=3, - user_id="sd53lsY=", - confidence_threshold=0.2, - short_answer_options=ShortAnswerOptions( - confidence_threshold=0.2, - top=1 - ), - include_unstructured_sources=True - ) - - assert len(output.answers) == 3 - confident_answers = [a for a in output.answers if a.confidence > 0.7] - assert len(confident_answers) == 1 - assert confident_answers[0].source == "surface-book-user-guide-EN.pdf" - - @pytest.mark.live_test_only("Needs re-recording to work with new common sanitizers") - def test_query_knowledgebase_with_followup(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - with client: - query_params = AnswersOptions( - question="How long should my Surface battery last?", - top=3, - user_id="sd53lsY=", - confidence_threshold=0.2, - short_answer_options=ShortAnswerOptions( - confidence_threshold=0.2, - top=1 - ), - include_unstructured_sources=True - ) - - output = client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - confident_answers = [a for a in output.answers if a.confidence > 0.7] - assert len(confident_answers) == 1 - assert confident_answers[0].source == "surface-book-user-guide-EN.pdf" + assert answer.short_answer.confidence is not None + def test_query_knowledgebase_filter(self, recorded_test, qna_creds): + filters = QueryFilters( + metadata_filter=MetadataFilter( + metadata=[ + MetadataRecord(key="explicitlytaggedheading", value="check the battery level"), + MetadataRecord(key="explicitlytaggedheading", value="make your battery last"), + ], + logical_operation="OR", + ), + ) + with QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) as client: query_params = AnswersOptions( - question="How long it takes to charge Surface?", + question="Battery life", + filters=filters, top=3, - user_id="sd53lsY=", - confidence_threshold=0.2, - answer_context=KnowledgeBaseAnswerContext( - previous_question="How long should my Surface battery last?", - previous_qna_id=confident_answers[0].qna_id - ), - short_answer_options=ShortAnswerOptions( - confidence_threshold=0.2, - top=1 - ), - include_unstructured_sources=True ) - output = client.get_answers( + response = client.get_answers( query_params, project_name=qna_creds["qna_project"], - deployment_name='test' + deployment_name="test", ) - - assert output.answers - confident_answers = [a for a in output.answers if a.confidence > 0.48] - assert len(confident_answers) == 1 - assert confident_answers[0].short_answer.text == " two to four hours" - + assert response.answers def test_query_knowledgebase_only_id(self, recorded_test, qna_creds): client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) with client: - query_params = AnswersOptions( - qna_id=19 - ) - - output = client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - + query_params = AnswersOptions(qna_id=19) + output = client.get_answers(query_params, project_name=qna_creds["qna_project"], deployment_name="test") + assert output.answers is not None assert len(output.answers) == 1 - def test_query_knowledgebase_python_dict(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - with client: - query_params = {"qna_id": 19} - - output = client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - - assert len(output.answers) == 1 - - def test_query_knowledgebase_overload_positional_and_kwarg(self): + def test_query_knowledgebase_overload_errors(self): # negative tests independent of live service with QuestionAnsweringClient("http://fake.com", AzureKeyCredential("123")) as client: + # These calls intentionally violate the method signature to ensure TypeError is raised. with pytest.raises(TypeError): - client.get_answers("positional_one", "positional_two") + client.get_answers("positional_one", "positional_two") # type: ignore with pytest.raises(TypeError): - client.get_answers("positional_options_bag", options="options bag by name") + client.get_answers("positional_options_bag", options="options bag by name") # type: ignore with pytest.raises(TypeError): - client.get_answers( - options={'qnaId': 15}, - project_name="hello", - deployment_name='test' - ) + client.get_answers(options={"qnaId": 15}, project_name="hello", deployment_name="test") # type: ignore with pytest.raises(TypeError): - client.get_answers( - {'qnaId': 15}, - question='Why?', - project_name="hello", - deployment_name='test' - ) + client.get_answers({"qnaId": 15}, question="Why?", project_name="hello", deployment_name="test") # type: ignore def test_query_knowledgebase_question_or_qna_id(self): with QuestionAnsweringClient("http://fake.com", AzureKeyCredential("123")) as client: - options = AnswersOptions() with pytest.raises(TypeError): - client.get_answers( - options, - project_name="hello", - deployment_name='test' - ) - + client.get_answers(options, project_name="hello", deployment_name="test") with pytest.raises(TypeError): - client.get_answers( - project_name="hello", - deployment_name='test' - ) - - def test_query_knowledgebase_filter(self, recorded_test, qna_creds): - """Thanks to @heaths for this test!""" - filters = QueryFilters( - metadata_filter=MetadataFilter( - metadata=[ - ("explicitlytaggedheading", "check the battery level"), - ("explicitlytaggedheading", "make your battery last") - ], - logical_operation="OR" - ), - ) - with QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) as client: - response = client.get_answers( - project_name=qna_creds["qna_project"], - deployment_name='test', - question="Battery life", - filters=filters, - top=3, - ) - assert len(response.answers) == 2 - assert any( - [a for a in response.answers if a.metadata.get('explicitlytaggedheading') == "check the battery level"] - ) - assert any( - [a for a in response.answers if a.metadata.get('explicitlytaggedheading') == "make your battery last"] - ) - - def test_query_knowledgebase_filter_dict_params(self, recorded_test, qna_creds): - filters = { - "metadataFilter": { - "metadata": [ - ("explicitlytaggedheading", "check the battery level"), - ("explicitlytaggedheading", "make your battery last") - ], - "logicalOperation": "or" - }, - } - with QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) as client: - response = client.get_answers( - project_name=qna_creds["qna_project"], - deployment_name='test', - question="Battery life", - filters=filters, - top=3, - ) - assert len(response.answers) == 2 - assert any( - [a for a in response.answers if a.metadata.get('explicitlytaggedheading') == "check the battery level"] - ) - assert any( - [a for a in response.answers if a.metadata.get('explicitlytaggedheading') == "make your battery last"] - ) + client.get_answers(project_name="hello", deployment_name="test") diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_knowledgebase_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_knowledgebase_async.py index 251aa22a0565..72b11cddd0e8 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_knowledgebase_async.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_knowledgebase_async.py @@ -1,469 +1,104 @@ +# pylint: disable=line-too-long,useless-suppression # coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ +# ------------------------------------------------------------------------- +# Runtime async tests: knowledge base querying (authoring removed) +# ------------------------------------------------------------------------- import pytest +from azure.ai.language.questionanswering.aio import QuestionAnsweringClient from azure.ai.language.questionanswering.models import ( AnswersOptions, KnowledgeBaseAnswerContext, ShortAnswerOptions, QueryFilters, MetadataFilter, + MetadataRecord, ) -from azure.ai.language.questionanswering.aio import QuestionAnsweringClient -from azure.ai.language.questionanswering._operations._operations import \ - build_question_answering_get_answers_request as build_get_answers_request from azure.core.credentials import AzureKeyCredential from testcase import QuestionAnsweringTestCase -class TestQnAKnowledgeBaseAsync(QuestionAnsweringTestCase): - - @pytest.mark.asyncio - async def test_query_knowledgebase_llc(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - json_content = { - "question": "Ports and connectors", - "top": 3, - "context": { - "previousUserQuery": "Meet Surface Pro 4", - "previousQnAId": 4 - } - } - request = build_get_answers_request( - json=json_content, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - async with client: - response = await client.send_request(request) - assert response.status_code == 200 - - output = response.json() - assert output - assert output.get('answers') - for answer in output['answers']: - assert answer.get('answer') - assert answer.get('confidenceScore') - assert answer.get('id') - assert answer.get('source') - assert answer.get('metadata') is not None - assert not answer.get('answerSpan') - - assert answer.get('questions') - for question in answer['questions']: - assert question - - assert answer.get('dialog') - assert answer['dialog'].get('isContextOnly') is not None - assert answer['dialog'].get('prompts') is not None - if answer['dialog'].get('prompts'): - for prompt in answer['dialog']['prompts']: - assert prompt.get('displayOrder') is not None - assert prompt.get('qnaId') - assert prompt.get('displayText') - - @pytest.mark.asyncio - async def test_query_knowledgebase_llc_with_answerspan(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - json_content = { - "question": "Ports and connectors", - "top": 3, - "context": { - "previousUserQuery": "Meet Surface Pro 4", - "previousQnAId": 4 - }, - "answerSpanRequest": { - "enable": True, - "confidenceScoreThreshold": 0.1, - "topAnswersWithSpan": 2 - } - } - request = build_get_answers_request( - json=json_content, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - async with client: - response = await client.send_request(request) - assert response.status_code == 200 - - output = response.json() - assert output - assert output.get('answers') - for answer in output['answers']: - assert answer.get('answer') - assert answer.get('confidenceScore') - assert answer.get('id') - assert answer.get('source') - assert answer.get('metadata') is not None - - if answer.get('answerSpan'): - assert answer['answerSpan'].get('text') - assert answer['answerSpan'].get('confidenceScore') - assert answer['answerSpan'].get('offset') is not None - assert answer['answerSpan'].get('length') - - assert answer.get('questions') - for question in answer['questions']: - assert question - - assert answer.get('dialog') - assert answer['dialog'].get('isContextOnly') is not None - assert answer['dialog'].get('prompts') is not None - if answer['dialog'].get('prompts'): - for prompt in answer['dialog']['prompts']: - assert prompt.get('displayOrder') is not None - assert prompt.get('qnaId') - assert prompt.get('displayText') - +class TestQueryKnowledgeBaseAsync(QuestionAnsweringTestCase): @pytest.mark.asyncio - async def test_query_knowledgebase(self, recorded_test, qna_creds): + async def test_query_knowledgebase_basic(self, recorded_test, qna_creds): client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - query_params = AnswersOptions( - question="Ports and connectors", - top=3, - answer_context=KnowledgeBaseAnswerContext( - previous_question="Meet Surface Pro 4", - previous_qna_id=4 - ) - ) - - async with client: - output = await client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - - assert output.answers - for answer in output.answers: - assert answer.answer - assert answer.confidence - assert answer.qna_id - assert answer.source - assert answer.metadata is not None - assert not answer.short_answer - - assert answer.questions - for question in answer.questions: - assert question - - assert answer.dialog - assert answer.dialog.is_context_only is not None - assert answer.dialog.prompts is not None - if answer.dialog.prompts: - for prompt in answer.dialog.prompts: - assert prompt.display_order is not None - assert prompt.qna_id - assert prompt.display_text - - @pytest.mark.asyncio - async def test_query_knowledgebase_aad(self, recorded_test, qna_creds): - token = self.get_credential(QuestionAnsweringClient, is_async=True) - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], token) - query_params = AnswersOptions( + params = AnswersOptions( question="Ports and connectors", top=3, - answer_context=KnowledgeBaseAnswerContext( - previous_question="Meet Surface Pro 4", - previous_qna_id=4 - ) + answer_context=KnowledgeBaseAnswerContext(previous_question="Meet Surface Pro 4", previous_qna_id=4), ) - async with client: - output = await client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - + output = await client.get_answers(params, project_name=qna_creds["qna_project"], deployment_name="production") assert output.answers for answer in output.answers: assert answer.answer - assert answer.confidence - assert answer.qna_id + assert answer.confidence is not None + assert answer.qna_id is not None assert answer.source assert answer.metadata is not None - assert not answer.short_answer - - assert answer.questions - for question in answer.questions: - assert question - - assert answer.dialog - assert answer.dialog.is_context_only is not None - assert answer.dialog.prompts is not None - if answer.dialog.prompts: - for prompt in answer.dialog.prompts: - assert prompt.display_order is not None - assert prompt.qna_id - assert prompt.display_text @pytest.mark.asyncio - async def test_query_knowledgebase_with_answerspan(self, recorded_test, qna_creds): + async def test_query_knowledgebase_with_short_answer(self, recorded_test, qna_creds): client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - query_params = AnswersOptions( + params = AnswersOptions( question="Ports and connectors", top=3, - answer_context=KnowledgeBaseAnswerContext( - previous_user_query="Meet Surface Pro 4", - previous_qna_id=4 - ), - short_answer_options=ShortAnswerOptions( - confidence_threshold=0.1, - top=2 - ) + answer_context=KnowledgeBaseAnswerContext(previous_question="Meet Surface Pro 4", previous_qna_id=4), + short_answer_options=ShortAnswerOptions(enable=True, confidence_threshold=0.1, top=2), ) - async with client: - output = await client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - + output = await client.get_answers(params, project_name=qna_creds["qna_project"], deployment_name="test") assert output.answers for answer in output.answers: - assert answer.answer - assert answer.confidence - assert answer.qna_id - assert answer.source - assert answer.metadata is not None - if answer.short_answer: assert answer.short_answer.text - assert answer.short_answer.confidence - assert answer.short_answer.offset is not None - assert answer.short_answer.length - - assert answer.questions - for question in answer.questions: - assert question - - assert answer.dialog - assert answer.dialog.is_context_only is not None - assert answer.dialog.prompts is not None - if answer.dialog.prompts: - for prompt in answer.dialog.prompts: - assert prompt.display_order is not None - assert prompt.qna_id - assert prompt.display_text - - @pytest.mark.live_test_only("Needs re-recording to work with new common sanitizers") - @pytest.mark.asyncio - async def test_query_knowledgebase_with_dictparams(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - query_params = { - "question": "How long should my Surface battery last?", - "top": 3, - "userId": "sd53lsY=", - "confidenceScoreThreshold": 0.2, - "answerSpanRequest": { - "enable": True, - "confidenceScoreThreshold": 0.2, - "topAnswersWithSpan": 1 - }, - "includeUnstructuredSources": True - } - - async with client: - output = await client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - - assert len(output.answers) == 3 - confident_answers = [a for a in output.answers if a.confidence > 0.7] - assert len(confident_answers) == 1 - assert confident_answers[0].source == "surface-book-user-guide-EN.pdf" - - @pytest.mark.live_test_only("Needs re-recording to work with new common sanitizers") - @pytest.mark.asyncio - async def test_query_knowledgebase_overload(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - async with client: - output = await client.get_answers( - project_name=qna_creds["qna_project"], - deployment_name='test', - question="How long should my Surface battery last?", - top=3, - user_id="sd53lsY=", - confidence_threshold=0.2, - short_answer_options=ShortAnswerOptions( - confidence_threshold=0.2, - top=1 - ), - include_unstructured_sources=True - ) - - assert len(output.answers) == 3 - confident_answers = [a for a in output.answers if a.confidence > 0.7] - assert len(confident_answers) == 1 - assert confident_answers[0].source == "surface-book-user-guide-EN.pdf" - - @pytest.mark.live_test_only("Needs re-recording to work with new common sanitizers") - @pytest.mark.asyncio - async def test_query_knowledgebase_with_followup(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - async with client: - query_params = AnswersOptions( - question="How long should my Surface battery last?", - top=3, - user_id="sd53lsY=", - confidence_threshold=0.2, - short_answer_options=ShortAnswerOptions( - confidence_threshold=0.2, - top=1 - ), - include_unstructured_sources=True - ) - - output = await client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - confident_answers = [a for a in output.answers if a.confidence > 0.7] - assert len(confident_answers) == 1 - assert confident_answers[0].source == "surface-book-user-guide-EN.pdf" - - query_params = AnswersOptions( - question="How long it takes to charge Surface?", - top=3, - user_id="sd53lsY=", - confidence_threshold=0.2, - answer_context=KnowledgeBaseAnswerContext( - previous_question="How long should my Surface battery last?", - previous_qna_id=confident_answers[0].qna_id - ), - short_answer_options=ShortAnswerOptions( - confidence_threshold=0.2, - top=1 - ), - include_unstructured_sources=True - ) - output = await client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - - assert output.answers - confident_answers = [a for a in output.answers if a.confidence > 0.48] - assert len(confident_answers) == 1 - assert confident_answers[0].short_answer.text == " two to four hours" - - @pytest.mark.asyncio - async def test_query_knowledgebase_only_id(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - async with client: - query_params = {"qnaId": 19} - - output = await client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - - assert len(output.answers) == 1 - - @pytest.mark.asyncio - async def test_query_knowledgebase_python_dict(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - async with client: - query_params = {"qna_id": 19} - - output = await client.get_answers( - query_params, - project_name=qna_creds["qna_project"], - deployment_name='test' - ) - - assert len(output.answers) == 1 - - @pytest.mark.asyncio - async def test_query_knowledgebase_overload_positional_and_kwarg(self): - async with QuestionAnsweringClient("http://fake.com", AzureKeyCredential("123")) as client: - with pytest.raises(TypeError): - await client.get_answers("positional_one", "positional_two") - with pytest.raises(TypeError): - await client.get_answers("positional_options_bag", options="options bag by name") - - @pytest.mark.asyncio - async def test_query_knowledgebase_question_or_qna_id(self): - async with QuestionAnsweringClient("http://fake.com", AzureKeyCredential("123")) as client: - - options = AnswersOptions() - with pytest.raises(TypeError): - await client.get_answers( - options, - project_name="hello", - deployment_name='test' - ) - - with pytest.raises(TypeError): - await client.get_answers( - project_name="hello", - deployment_name='test' - ) + assert answer.short_answer.confidence is not None @pytest.mark.asyncio async def test_query_knowledgebase_filter(self, recorded_test, qna_creds): - """Thanks to @heaths for this test!""" filters = QueryFilters( metadata_filter=MetadataFilter( metadata=[ - ("explicitlytaggedheading", "check the battery level"), - ("explicitlytaggedheading", "make your battery last") + MetadataRecord(key="explicitlytaggedheading", value="check the battery level"), + MetadataRecord(key="explicitlytaggedheading", value="make your battery last"), ], - logical_operation="OR" + logical_operation="OR", ) ) - async with QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) as client: - response = await client.get_answers( - project_name=qna_creds["qna_project"], - deployment_name='test', + async with QuestionAnsweringClient( + qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"]) + ) as client: + params = AnswersOptions( question="Battery life", - filters=filters, top=3, + filters=filters, ) - assert len(response.answers) == 2 - assert any( - [a for a in response.answers if a.metadata.get('explicitlytaggedheading') == "check the battery level"] - ) - assert any( - [a for a in response.answers if a.metadata.get('explicitlytaggedheading') == "make your battery last"] - ) - - @pytest.mark.asyncio - async def test_query_knowledgebase_filter_dict_params(self, recorded_test, qna_creds): - filters = { - "metadataFilter": { - "metadata": [ - ("explicitlytaggedheading", "check the battery level"), - ("explicitlytaggedheading", "make your battery last") - ], - "logicalOperation": "or" - }, - - } - async with QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) as client: response = await client.get_answers( + params, project_name=qna_creds["qna_project"], - deployment_name='test', - question="Battery life", - filters=filters, - top=3, + deployment_name="production", ) - assert len(response.answers) == 2 + assert response.answers assert any( - [a for a in response.answers if a.metadata.get('explicitlytaggedheading') == "check the battery level"] + [ + a + for a in response.answers + if (a.metadata or {}).get("explicitlytaggedheading") == "check the battery level" + ] ) assert any( - [a for a in response.answers if a.metadata.get('explicitlytaggedheading') == "make your battery last"] + [ + a + for a in response.answers + if (a.metadata or {}).get("explicitlytaggedheading") == "make your battery last" + ] ) + + @pytest.mark.asyncio + async def test_query_knowledgebase_overload_errors(self): # negative parameter validation + async with QuestionAnsweringClient("http://fake.com", AzureKeyCredential("123")) as client: + with pytest.raises(TypeError): + await client.get_answers("positional_one", "positional_two") # type: ignore[arg-type] + with pytest.raises(TypeError): + await client.get_answers("positional_options_bag", options="options bag by name") # type: ignore[arg-type] diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_text.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_text.py index 502164365ab8..8a9a55f98e8c 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_text.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_text.py @@ -1,276 +1,113 @@ +# pylint: disable=line-too-long,useless-suppression # coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ +# ------------------------------------------------------------------------- +# Runtime tests: text records querying (authoring removed) +# ------------------------------------------------------------------------- import pytest from azure.ai.language.questionanswering import QuestionAnsweringClient -from azure.ai.language.questionanswering._operations._operations import \ - build_question_answering_get_answers_from_text_request as build_get_answers_from_text_request -from azure.ai.language.questionanswering.models import ( - AnswersFromTextOptions, - TextDocument -) +from azure.ai.language.questionanswering.models import AnswersFromTextOptions, TextDocument from azure.core.credentials import AzureKeyCredential from testcase import QuestionAnsweringTestCase class TestQueryText(QuestionAnsweringTestCase): - - def test_query_text_llc(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - json_content = { - "question": "What is the meaning of life?", - "records": [ - { - "text": "abc Graphics Surprise, surprise -- our 4K ", - "id": "doc1" - }, - { - "text": "e graphics card. While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. The MX250-equipped Envy 13 scored a 116,575 on the Ice Storm Unlimited benchmark while the base model scored a 82,270. Upgrading to the discrete graphics gives the Envy 13 better performance than the Notebook 9 Pro (61,662; UHD 620), Surface Laptop 2 (71,647; UHD 620) and the premium laptop average (86,937). While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. We played the racing game Dirt 3 at 92 frames per second on ", - "id": "doc2" - }, - { - "text": "Graphics Surprise, surprise -- our 4K Envy 13 came with a discrete graphics card. While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. The MX250-equipped Envy 13 scored a 116,575 on the Ice Storm Unlimited benchmark while the base model scored a 82,270. Upgrading to the discrete graphics gives the Envy 13 better performance than the Notebook 9 Pro (61,662; UHD 620), Surface Laptop 2 (71,647; UHD 620) and the premium laptop average (86,937). While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. We played the racing game Dirt 3 at 92 frames per second on the MX250 model, which is well above our 30-fps playability, the category average (69 fps) and what the Surface Laptop 2 (82 fps) achieved. The ZenBook S UX391UA (45 fps) fell flat on this real-world test but ran better than the base model Envy 13 (31 fps). Audio I had a good ol' time groovin' to the sound of the Envy 13's crisp speakers. HP went all out with the Envy, placing dual speakers on the underside of the chassis along with a third, top-firing driver above the keyboard. Devon Gilfillian's funky jam \"Here and Now\" boomed smooth, soulful tunes throughout my small apartment. The twang of the electric guitar played nicely with the thudding percussion but never overshadowed Gilfillian or the female backup vocals. Bang & Olufsen software comes preinstalled on the Envy 13, with equalizer controls so you can adjust the bass, midrange and treble to your liking. But even out of the box, you'll enjoy great sound without having to bust out your headphones. Battery Life Get an Envy 13 with the 1080p non-touch display if battery life is important to you. The FHD model endured for 11 hours and 11 minutes whereas the 4K model lasted only 4 hours and 36 minutes on our battery test, which involves continuous web browsing over Wi-Fi at 150 nits of brightness. MORE: Laptops with Best Battery Life - Longest Lasting Laptop Batteries Competing laptops like the ZenBook S UX391UA (7:05), Surface Laptop 2 (9:22) and Notebook 9 Pro (8:53) outstayed the 4K Envy 13 but powered down long before the 1080p version. Webcam The 720p webcam on the Envy 13 is nothing to write home about. A selfie I snapped in my dimly lit room was covered in a haze of visual noise. My beard and hair were unkempt blobs, while my eyes looked like they were drawn on by a pointillist painter. If there's one positive, it's that the lens captures natural colors and even extracted the different shades of gray in my T-shirt. On the right edge of the Envy 13 is a physical kill switch that cuts the power to the webcam so you can feel reassured that nobody is snooping on you. Heat Leave the lapdesk at home - you don't have to worry about the Envy 13 overheating. After I played a 15-minute, full-HD video in full screen, the touchpad on the HP Envy 13 with a Core i7 CPU rose to only 83 degrees Fahrenheit while the keyboard (87 degrees) and underside (90 degrees) also remained well below our 95-degree comfort threshold. Even the toastiest part of the machine, the lower-left edge on the underside, topped out at 94 degrees. Software and Warranty It's a shame that a laptop with such beautiful hardware ships with such ugly software. Pre-installed on this machine are entirely too many programs that could either be packaged together or omitted altogether. HP provides an app called Audio Switch, which simply lets you switch your audio input/output between the internal speakers and headphones. As the same implies, HP's Command Center is where you can get information about your Envy 13 but also switch the thermal profiles between comfort and performance. Along with support documentation, HP also bundles in a setup program called JumpStart, a program for connecting printers and a redundant system-info app called Event Utility. Also installed on the Envy 13's Windows 10 Home OS are several Microsoft apps, including Simple Solitaire, Candy Crush Friends and Your Phone. Other third-party apps include Booking.com, Netflix and McAfee Security. HP ships the Envy 13 with a one-year warranty. See how HP did on our Tech Support Showdown and Best and Worst Brands ranking. Bottom Line The Envy 13 has cemented its standing as the ultimate laptop for college students or travelers. Along with 11-plus hours of battery life (on the FHD model), the Envy 13 has a sleek, ultraportable chassis, fast performance, and powerful speakers. Best of all, the Envy 13 starts at a reasonable $799, which is hundreds less than the competition. In many ways, the Envy 13 is what we wanted the new MacBook Air to be. The new HP Envy 13 is everything I was hoping the new MacBook Air would be: fast, attractive and affordable. Just be sure to buy the right model. We strongly recommend the 1080p version over the 4K model because it lasts several hours longer on a charge and costs less. In fact, if we were reviewing the 4K model separately, we'd only give it a 3.5 rating. You should also consider the Envy 13 with a 10th Gen CPU, although we haven't gotten the chance to review it yet. If you absolutely need a high-res display, the 4K Envy 13 is one of many good options. We also recommend the Samsung Notebook 9 Pro, which has a similarly premium design but much better battery life than the 4K Envy. The Microsoft Surface Laptop 2 is another recommended alternative, though you might want to wait a few months for the rumored Surface Laptop 3. Overall, the HP Envy 13 is a fantastic laptop that checks all the right boxes --- as long as you buy the 1080p model. Credit: Laptop Mag HP Envy 13 (2019) Specs BluetoothBluetooth 5.0 BrandHP CPUIntel Core i7-8565U Card SlotsmicroSD Company Websitehttps://www8.hp.com/us/en/home.html Display Size13.3 Graphics CardNvidia GeForce MX250 Hard Drive Size512GB Hard Drive TypePCIe NVMe M.2 Highest Available Resolution3840 x 2160 Native Resolution3840 x 2160 Operating SystemWindows 10 Home Ports (excluding USB)USB 3.1 with Type-C, USB 3.1 Always-On, USB 3.1, Headphone/Mic, microSD RAM16GB RAM Upgradable to16GB Size12.1 x 8.3 x .57 inches Touchpad Size4.3 x 2.2 inches USB Ports3 Video Memory2GB Warranty/Supportone-year warranty. Weight2.8 pounds Wi-Fi802.11ac Wi-Fi ModelIntel Wireless-AC 9560 ", - "id": "doc3" - } - ], - "language": "en" - } - request = build_get_answers_from_text_request( - json=json_content - ) - response = client.send_request(request) - assert response.status_code == 200 - - output = response.json() - assert output.get('answers') - for answer in output['answers']: - assert answer.get('answer') - assert answer.get('confidenceScore') - assert answer.get('id') - assert answer.get('offset') - assert answer.get('length') - assert answer.get('answerSpan') - assert answer['answerSpan'].get('text') - assert answer['answerSpan'].get('confidenceScore') - assert answer['answerSpan'].get('offset') is not None - assert answer['answerSpan'].get('length') - - def test_query_text(self, recorded_test, qna_creds): + def test_query_text_basic(self, recorded_test, qna_creds): client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) params = AnswersFromTextOptions( question="What is the meaning of life?", text_documents=[ - TextDocument( - text="abc Graphics Surprise, surprise -- our 4K ", - id="doc1" - ), - TextDocument( - text="e graphics card. While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. The MX250-equipped Envy 13 scored a 116,575 on the Ice Storm Unlimited benchmark while the base model scored a 82,270. Upgrading to the discrete graphics gives the Envy 13 better performance than the Notebook 9 Pro (61,662; UHD 620), Surface Laptop 2 (71,647; UHD 620) and the premium laptop average (86,937). While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. We played the racing game Dirt 3 at 92 frames per second on ", - id="doc2" - ), - TextDocument( - text="Graphics Surprise, surprise -- our 4K Envy 13 came with a discrete graphics card. While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. The MX250-equipped Envy 13 scored a 116,575 on the Ice Storm Unlimited benchmark while the base model scored a 82,270. Upgrading to the discrete graphics gives the Envy 13 better performance than the Notebook 9 Pro (61,662; UHD 620), Surface Laptop 2 (71,647; UHD 620) and the premium laptop average (86,937). While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. We played the racing game Dirt 3 at 92 frames per second on the MX250 model, which is well above our 30-fps playability, the category average (69 fps) and what the Surface Laptop 2 (82 fps) achieved. The ZenBook S UX391UA (45 fps) fell flat on this real-world test but ran better than the base model Envy 13 (31 fps). Audio I had a good ol' time groovin' to the sound of the Envy 13's crisp speakers. HP went all out with the Envy, placing dual speakers on the underside of the chassis along with a third, top-firing driver above the keyboard. Devon Gilfillian's funky jam \"Here and Now\" boomed smooth, soulful tunes throughout my small apartment. The twang of the electric guitar played nicely with the thudding percussion but never overshadowed Gilfillian or the female backup vocals. Bang & Olufsen software comes preinstalled on the Envy 13, with equalizer controls so you can adjust the bass, midrange and treble to your liking. But even out of the box, you'll enjoy great sound without having to bust out your headphones. Battery Life Get an Envy 13 with the 1080p non-touch display if battery life is important to you. The FHD model endured for 11 hours and 11 minutes whereas the 4K model lasted only 4 hours and 36 minutes on our battery test, which involves continuous web browsing over Wi-Fi at 150 nits of brightness. MORE: Laptops with Best Battery Life - Longest Lasting Laptop Batteries Competing laptops like the ZenBook S UX391UA (7:05), Surface Laptop 2 (9:22) and Notebook 9 Pro (8:53) outstayed the 4K Envy 13 but powered down long before the 1080p version. Webcam The 720p webcam on the Envy 13 is nothing to write home about. A selfie I snapped in my dimly lit room was covered in a haze of visual noise. My beard and hair were unkempt blobs, while my eyes looked like they were drawn on by a pointillist painter. If there's one positive, it's that the lens captures natural colors and even extracted the different shades of gray in my T-shirt. On the right edge of the Envy 13 is a physical kill switch that cuts the power to the webcam so you can feel reassured that nobody is snooping on you. Heat Leave the lapdesk at home - you don't have to worry about the Envy 13 overheating. After I played a 15-minute, full-HD video in full screen, the touchpad on the HP Envy 13 with a Core i7 CPU rose to only 83 degrees Fahrenheit while the keyboard (87 degrees) and underside (90 degrees) also remained well below our 95-degree comfort threshold. Even the toastiest part of the machine, the lower-left edge on the underside, topped out at 94 degrees. Software and Warranty It's a shame that a laptop with such beautiful hardware ships with such ugly software. Pre-installed on this machine are entirely too many programs that could either be packaged together or omitted altogether. HP provides an app called Audio Switch, which simply lets you switch your audio input/output between the internal speakers and headphones. As the same implies, HP's Command Center is where you can get information about your Envy 13 but also switch the thermal profiles between comfort and performance. Along with support documentation, HP also bundles in a setup program called JumpStart, a program for connecting printers and a redundant system-info app called Event Utility. Also installed on the Envy 13's Windows 10 Home OS are several Microsoft apps, including Simple Solitaire, Candy Crush Friends and Your Phone. Other third-party apps include Booking.com, Netflix and McAfee Security. HP ships the Envy 13 with a one-year warranty. See how HP did on our Tech Support Showdown and Best and Worst Brands ranking. Bottom Line The Envy 13 has cemented its standing as the ultimate laptop for college students or travelers. Along with 11-plus hours of battery life (on the FHD model), the Envy 13 has a sleek, ultraportable chassis, fast performance, and powerful speakers. Best of all, the Envy 13 starts at a reasonable $799, which is hundreds less than the competition. In many ways, the Envy 13 is what we wanted the new MacBook Air to be. The new HP Envy 13 is everything I was hoping the new MacBook Air would be: fast, attractive and affordable. Just be sure to buy the right model. We strongly recommend the 1080p version over the 4K model because it lasts several hours longer on a charge and costs less. In fact, if we were reviewing the 4K model separately, we'd only give it a 3.5 rating. You should also consider the Envy 13 with a 10th Gen CPU, although we haven't gotten the chance to review it yet. If you absolutely need a high-res display, the 4K Envy 13 is one of many good options. We also recommend the Samsung Notebook 9 Pro, which has a similarly premium design but much better battery life than the 4K Envy. The Microsoft Surface Laptop 2 is another recommended alternative, though you might want to wait a few months for the rumored Surface Laptop 3. Overall, the HP Envy 13 is a fantastic laptop that checks all the right boxes --- as long as you buy the 1080p model. Credit: Laptop Mag HP Envy 13 (2019) Specs BluetoothBluetooth 5.0 BrandHP CPUIntel Core i7-8565U Card SlotsmicroSD Company Websitehttps://www8.hp.com/us/en/home.html Display Size13.3 Graphics CardNvidia GeForce MX250 Hard Drive Size512GB Hard Drive TypePCIe NVMe M.2 Highest Available Resolution3840 x 2160 Native Resolution3840 x 2160 Operating SystemWindows 10 Home Ports (excluding USB)USB 3.1 with Type-C, USB 3.1 Always-On, USB 3.1, Headphone/Mic, microSD RAM16GB RAM Upgradable to16GB Size12.1 x 8.3 x .57 inches Touchpad Size4.3 x 2.2 inches USB Ports3 Video Memory2GB Warranty/Supportone-year warranty. Weight2.8 pounds Wi-Fi802.11ac Wi-Fi ModelIntel Wireless-AC 9560 ", - id="doc3" - ) + TextDocument(text="abc Graphics Surprise, surprise -- our 4K ", id="doc1"), + TextDocument(text="Short text about battery life and charging speed.", id="doc2"), ], - language="en" + language="en", ) - + # Use operations group directly to satisfy static type checking (dynamic convenience wrapper added in _patch) output = client.get_answers_from_text(params) assert output.answers for answer in output.answers: assert answer.answer - assert answer.confidence - assert answer.id - assert answer.offset - assert answer.length - assert answer.short_answer - assert answer.short_answer.text - assert answer.short_answer.confidence - assert answer.short_answer.offset is not None - assert answer.short_answer.length + assert answer.confidence is not None + assert answer.id is not None + if answer.short_answer: + assert answer.short_answer.text - def test_query_text_with_dictparams(self, recorded_test, qna_creds): + def test_query_text_with_str_records(self, recorded_test, qna_creds): client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) params = { "question": "How long it takes to charge surface?", + # Wire field must be 'records' per AnswersFromTextOptions (text_documents -> records) "records": [ { - "text": "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - "id": "1" + "text": "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully.", + "id": "r1", }, { - "text": "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. "+ - "The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface.", - "id": "2" - } - ], - "language": "en" - } - - with client: - output = client.get_answers_from_text(params) - assert len(output.answers) == 3 - confident_answers = [a for a in output.answers if a.confidence > 0.9] - assert len(confident_answers) == 2 - assert confident_answers[0].short_answer.text == "two to four hours" - - def test_query_text_with_str_records(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - params = { - "question": "How long it takes to charge surface?", - "records": [ - "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. "+ - "The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface.", + "text": "You can use the USB port on your Surface Pro 4 power supply to charge other devices.", + "id": "r2", + }, ], - "language": "en" + "language": "en", } - with client: output = client.get_answers_from_text(params) - assert len(output.answers) == 3 - confident_answers = [a for a in output.answers if a.confidence > 0.9] - assert len(confident_answers) == 2 - assert confident_answers[0].short_answer.text == "two to four hours" - - def test_query_text_overload(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - with client: - with pytest.raises(TypeError): - client.get_answers_from_text( - question="How long it takes to charge surface?", - text_documents=[ - "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - { - "text": "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. "+ - "The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface.", - "id": "2" - } - ] - ) - output = client.get_answers_from_text( - question="How long it takes to charge surface?", - text_documents=[ - "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. "+ - "The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface.", - ] - ) - assert len(output.answers) == 3 - confident_answers = [a for a in output.answers if a.confidence > 0.9] - assert len(confident_answers) == 2 - assert confident_answers[0].short_answer.text == "two to four hours" + assert output.answers - def test_query_text_overload_positional_and_kwarg(self): + def test_query_text_overload_errors(self): # negative client-side parameter validation with QuestionAnsweringClient("http://fake.com", AzureKeyCredential("123")) as client: with pytest.raises(TypeError): - client.get_answers_from_text("positional_one", "positional_two") + client.get_answers_from_text("positional_one", "positional_two") # type: ignore[arg-type] with pytest.raises(TypeError): - client.get_answers_from_text("positional_options_bag", options="options bag by name") - + client.get_answers_from_text("positional_options_bag", options="options bag by name") # type: ignore[arg-type] params = AnswersFromTextOptions( - question="What is the meaning of life?", - text_documents=[ - TextDocument( - text="foo", - id="doc1" - ), - TextDocument( - text="bar", - id="doc2" - ) - ], - language="en" + question="Meaning?", + text_documents=[TextDocument(text="foo", id="doc1"), TextDocument(text="bar", id="doc2")], ) with pytest.raises(TypeError): - client.get_answers_from_text(options=params) - + client.get_answers_from_text(options=params) # type: ignore[arg-type] with pytest.raises(TypeError): - client.get_answers_from_text( - question="why?", - text_documents=["foo", "bar"], - options=params) - + client.get_answers_from_text(question="why?", text_documents=["foo", "bar"], options=params) # type: ignore[arg-type] with pytest.raises(TypeError): - client.get_answers_from_text(params, question="Why?") + client.get_answers_from_text(params, question="Why?") # type: ignore[arg-type] - def test_query_text_default_lang(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"]), default_language="es") - params = { - "question": "How long it takes to charge surface?", - "records": [ - { - "text": "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - "id": "1" - }, - ], - } - - param_model = AnswersFromTextOptions( - question="How long it takes to charge surface?", - text_documents=[ - TextDocument( - text="Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - id="1" - ), - ], + def test_query_text_default_lang_override(self, recorded_test, qna_creds): + client = QuestionAnsweringClient( + qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"]), default_language="es" ) - - def callback_es(response): - import json - resp = json.loads(response.http_request.content) - assert resp["language"] == "es" - - def callback_en(response): - import json - resp = json.loads(response.http_request.content) - assert resp["language"] == "en" - - # --- dict request --- - # check client default language - output = client.get_answers_from_text(params, raw_response_hook=callback_es) - - # check that operation-level language overrides client-level default lang - params["language"] = "en" - output = client.get_answers_from_text(params, raw_response_hook=callback_en) - - # --- model request --- - # check client default language - output = client.get_answers_from_text(param_model, raw_response_hook=callback_es) - - # check that operation-level language overrides client-level default lang - param_model.language = "en" - output = client.get_answers_from_text(param_model, raw_response_hook=callback_en) - - # --- kwargs request --- - # check client default language + # client default applies output = client.get_answers_from_text( - question="How long it takes to charge surface?", - text_documents=[ - "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - ], - raw_response_hook=callback_es + { + "question": "How long it takes to charge surface?", + "language": "es", + "records": [ + { + "text": "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully.", + "id": "doc1", + } + ], + }, + raw_response_hook=lambda r: _assert_request_language(r, "es"), ) - # check that operation-level language overrides client-level default lang + # override output = client.get_answers_from_text( - question="How long it takes to charge surface?", - text_documents=[ - "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - ], - language="en", - raw_response_hook=callback_en + { + "question": "How long it takes to charge surface?", + "language": "en", + "records": [ + { + "text": "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully.", + "id": "doc1", + } + ], + }, + raw_response_hook=lambda r: _assert_request_language(r, "en"), ) + assert output is not None + + +def _assert_request_language(response, expected): + import json + + body = json.loads(response.http_request.content) + assert body.get("language") == expected diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_text_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_text_async.py index c0e429a99962..7eec7b126fe8 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_text_async.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_query_text_async.py @@ -1,13 +1,11 @@ +# pylint: disable=line-too-long,useless-suppression # coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ +# ------------------------------------------------------------------------- +# Inference async tests: text records querying (authoring removed) +# ------------------------------------------------------------------------- import pytest from azure.ai.language.questionanswering.aio import QuestionAnsweringClient -from azure.ai.language.questionanswering._operations._operations import \ - build_question_answering_get_answers_from_text_request as build_get_answers_from_text_request from azure.ai.language.questionanswering.models import AnswersFromTextOptions, TextDocument from azure.core.credentials import AzureKeyCredential @@ -15,168 +13,104 @@ class TestQueryTextAsync(QuestionAnsweringTestCase): - - @pytest.mark.asyncio - async def test_query_text_llc(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - json_content = { - "question": "What is the meaning of life?", - "records": [ - { - "text": "abc Graphics Surprise, surprise -- our 4K ", - "id": "doc1" - }, - { - "text": "e graphics card. While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. The MX250-equipped Envy 13 scored a 116,575 on the Ice Storm Unlimited benchmark while the base model scored a 82,270. Upgrading to the discrete graphics gives the Envy 13 better performance than the Notebook 9 Pro (61,662; UHD 620), Surface Laptop 2 (71,647; UHD 620) and the premium laptop average (86,937). While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. We played the racing game Dirt 3 at 92 frames per second on ", - "id": "doc2" - }, - { - "text": "Graphics Surprise, surprise -- our 4K Envy 13 came with a discrete graphics card. While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. The MX250-equipped Envy 13 scored a 116,575 on the Ice Storm Unlimited benchmark while the base model scored a 82,270. Upgrading to the discrete graphics gives the Envy 13 better performance than the Notebook 9 Pro (61,662; UHD 620), Surface Laptop 2 (71,647; UHD 620) and the premium laptop average (86,937). While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. We played the racing game Dirt 3 at 92 frames per second on the MX250 model, which is well above our 30-fps playability, the category average (69 fps) and what the Surface Laptop 2 (82 fps) achieved. The ZenBook S UX391UA (45 fps) fell flat on this real-world test but ran better than the base model Envy 13 (31 fps). Audio I had a good ol' time groovin' to the sound of the Envy 13's crisp speakers. HP went all out with the Envy, placing dual speakers on the underside of the chassis along with a third, top-firing driver above the keyboard. Devon Gilfillian's funky jam \"Here and Now\" boomed smooth, soulful tunes throughout my small apartment. The twang of the electric guitar played nicely with the thudding percussion but never overshadowed Gilfillian or the female backup vocals. Bang & Olufsen software comes preinstalled on the Envy 13, with equalizer controls so you can adjust the bass, midrange and treble to your liking. But even out of the box, you'll enjoy great sound without having to bust out your headphones. Battery Life Get an Envy 13 with the 1080p non-touch display if battery life is important to you. The FHD model endured for 11 hours and 11 minutes whereas the 4K model lasted only 4 hours and 36 minutes on our battery test, which involves continuous web browsing over Wi-Fi at 150 nits of brightness. MORE: Laptops with Best Battery Life - Longest Lasting Laptop Batteries Competing laptops like the ZenBook S UX391UA (7:05), Surface Laptop 2 (9:22) and Notebook 9 Pro (8:53) outstayed the 4K Envy 13 but powered down long before the 1080p version. Webcam The 720p webcam on the Envy 13 is nothing to write home about. A selfie I snapped in my dimly lit room was covered in a haze of visual noise. My beard and hair were unkempt blobs, while my eyes looked like they were drawn on by a pointillist painter. If there's one positive, it's that the lens captures natural colors and even extracted the different shades of gray in my T-shirt. On the right edge of the Envy 13 is a physical kill switch that cuts the power to the webcam so you can feel reassured that nobody is snooping on you. Heat Leave the lapdesk at home - you don't have to worry about the Envy 13 overheating. After I played a 15-minute, full-HD video in full screen, the touchpad on the HP Envy 13 with a Core i7 CPU rose to only 83 degrees Fahrenheit while the keyboard (87 degrees) and underside (90 degrees) also remained well below our 95-degree comfort threshold. Even the toastiest part of the machine, the lower-left edge on the underside, topped out at 94 degrees. Software and Warranty It's a shame that a laptop with such beautiful hardware ships with such ugly software. Pre-installed on this machine are entirely too many programs that could either be packaged together or omitted altogether. HP provides an app called Audio Switch, which simply lets you switch your audio input/output between the internal speakers and headphones. As the same implies, HP's Command Center is where you can get information about your Envy 13 but also switch the thermal profiles between comfort and performance. Along with support documentation, HP also bundles in a setup program called JumpStart, a program for connecting printers and a redundant system-info app called Event Utility. Also installed on the Envy 13's Windows 10 Home OS are several Microsoft apps, including Simple Solitaire, Candy Crush Friends and Your Phone. Other third-party apps include Booking.com, Netflix and McAfee Security. HP ships the Envy 13 with a one-year warranty. See how HP did on our Tech Support Showdown and Best and Worst Brands ranking. Bottom Line The Envy 13 has cemented its standing as the ultimate laptop for college students or travelers. Along with 11-plus hours of battery life (on the FHD model), the Envy 13 has a sleek, ultraportable chassis, fast performance, and powerful speakers. Best of all, the Envy 13 starts at a reasonable $799, which is hundreds less than the competition. In many ways, the Envy 13 is what we wanted the new MacBook Air to be. The new HP Envy 13 is everything I was hoping the new MacBook Air would be: fast, attractive and affordable. Just be sure to buy the right model. We strongly recommend the 1080p version over the 4K model because it lasts several hours longer on a charge and costs less. In fact, if we were reviewing the 4K model separately, we'd only give it a 3.5 rating. You should also consider the Envy 13 with a 10th Gen CPU, although we haven't gotten the chance to review it yet. If you absolutely need a high-res display, the 4K Envy 13 is one of many good options. We also recommend the Samsung Notebook 9 Pro, which has a similarly premium design but much better battery life than the 4K Envy. The Microsoft Surface Laptop 2 is another recommended alternative, though you might want to wait a few months for the rumored Surface Laptop 3. Overall, the HP Envy 13 is a fantastic laptop that checks all the right boxes --- as long as you buy the 1080p model. Credit: Laptop Mag HP Envy 13 (2019) Specs BluetoothBluetooth 5.0 BrandHP CPUIntel Core i7-8565U Card SlotsmicroSD Company Websitehttps://www8.hp.com/us/en/home.html Display Size13.3 Graphics CardNvidia GeForce MX250 Hard Drive Size512GB Hard Drive TypePCIe NVMe M.2 Highest Available Resolution3840 x 2160 Native Resolution3840 x 2160 Operating SystemWindows 10 Home Ports (excluding USB)USB 3.1 with Type-C, USB 3.1 Always-On, USB 3.1, Headphone/Mic, microSD RAM16GB RAM Upgradable to16GB Size12.1 x 8.3 x .57 inches Touchpad Size4.3 x 2.2 inches USB Ports3 Video Memory2GB Warranty/Supportone-year warranty. Weight2.8 pounds Wi-Fi802.11ac Wi-Fi ModelIntel Wireless-AC 9560 ", - "id": "doc3" - } - ], - "language": "en" - } - request = build_get_answers_from_text_request( - json=json_content - ) - response = await client.send_request(request) - assert response.status_code == 200 - - output = response.json() - assert output.get('answers') - for answer in output['answers']: - assert answer.get('answer') - assert answer.get('confidenceScore') - assert answer.get('id') - assert answer.get('offset') - assert answer.get('length') - assert answer.get('answerSpan') - assert answer['answerSpan'].get('text') - assert answer['answerSpan'].get('confidenceScore') - assert answer['answerSpan'].get('offset') is not None - assert answer['answerSpan'].get('length') - @pytest.mark.asyncio - async def test_query_text(self, recorded_test, qna_creds): + async def test_query_text_basic(self, recorded_test, qna_creds): client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) params = AnswersFromTextOptions( question="What is the meaning of life?", text_documents=[ - TextDocument( - text="abc Graphics Surprise, surprise -- our 4K ", - id="doc1" - ), - TextDocument( - text="e graphics card. While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. The MX250-equipped Envy 13 scored a 116,575 on the Ice Storm Unlimited benchmark while the base model scored a 82,270. Upgrading to the discrete graphics gives the Envy 13 better performance than the Notebook 9 Pro (61,662; UHD 620), Surface Laptop 2 (71,647; UHD 620) and the premium laptop average (86,937). While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. We played the racing game Dirt 3 at 92 frames per second on ", - id="doc2" - ), - TextDocument( - text="Graphics Surprise, surprise -- our 4K Envy 13 came with a discrete graphics card. While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. The MX250-equipped Envy 13 scored a 116,575 on the Ice Storm Unlimited benchmark while the base model scored a 82,270. Upgrading to the discrete graphics gives the Envy 13 better performance than the Notebook 9 Pro (61,662; UHD 620), Surface Laptop 2 (71,647; UHD 620) and the premium laptop average (86,937). While the Nvidia GeForce MX250 GPU isn't meant for demanding gaming, it is a step up from integrated graphics as proven by comparing it to the UHD 620 GPU in the FHD model. We played the racing game Dirt 3 at 92 frames per second on the MX250 model, which is well above our 30-fps playability, the category average (69 fps) and what the Surface Laptop 2 (82 fps) achieved. The ZenBook S UX391UA (45 fps) fell flat on this real-world test but ran better than the base model Envy 13 (31 fps). Audio I had a good ol' time groovin' to the sound of the Envy 13's crisp speakers. HP went all out with the Envy, placing dual speakers on the underside of the chassis along with a third, top-firing driver above the keyboard. Devon Gilfillian's funky jam \"Here and Now\" boomed smooth, soulful tunes throughout my small apartment. The twang of the electric guitar played nicely with the thudding percussion but never overshadowed Gilfillian or the female backup vocals. Bang & Olufsen software comes preinstalled on the Envy 13, with equalizer controls so you can adjust the bass, midrange and treble to your liking. But even out of the box, you'll enjoy great sound without having to bust out your headphones. Battery Life Get an Envy 13 with the 1080p non-touch display if battery life is important to you. The FHD model endured for 11 hours and 11 minutes whereas the 4K model lasted only 4 hours and 36 minutes on our battery test, which involves continuous web browsing over Wi-Fi at 150 nits of brightness. MORE: Laptops with Best Battery Life - Longest Lasting Laptop Batteries Competing laptops like the ZenBook S UX391UA (7:05), Surface Laptop 2 (9:22) and Notebook 9 Pro (8:53) outstayed the 4K Envy 13 but powered down long before the 1080p version. Webcam The 720p webcam on the Envy 13 is nothing to write home about. A selfie I snapped in my dimly lit room was covered in a haze of visual noise. My beard and hair were unkempt blobs, while my eyes looked like they were drawn on by a pointillist painter. If there's one positive, it's that the lens captures natural colors and even extracted the different shades of gray in my T-shirt. On the right edge of the Envy 13 is a physical kill switch that cuts the power to the webcam so you can feel reassured that nobody is snooping on you. Heat Leave the lapdesk at home - you don't have to worry about the Envy 13 overheating. After I played a 15-minute, full-HD video in full screen, the touchpad on the HP Envy 13 with a Core i7 CPU rose to only 83 degrees Fahrenheit while the keyboard (87 degrees) and underside (90 degrees) also remained well below our 95-degree comfort threshold. Even the toastiest part of the machine, the lower-left edge on the underside, topped out at 94 degrees. Software and Warranty It's a shame that a laptop with such beautiful hardware ships with such ugly software. Pre-installed on this machine are entirely too many programs that could either be packaged together or omitted altogether. HP provides an app called Audio Switch, which simply lets you switch your audio input/output between the internal speakers and headphones. As the same implies, HP's Command Center is where you can get information about your Envy 13 but also switch the thermal profiles between comfort and performance. Along with support documentation, HP also bundles in a setup program called JumpStart, a program for connecting printers and a redundant system-info app called Event Utility. Also installed on the Envy 13's Windows 10 Home OS are several Microsoft apps, including Simple Solitaire, Candy Crush Friends and Your Phone. Other third-party apps include Booking.com, Netflix and McAfee Security. HP ships the Envy 13 with a one-year warranty. See how HP did on our Tech Support Showdown and Best and Worst Brands ranking. Bottom Line The Envy 13 has cemented its standing as the ultimate laptop for college students or travelers. Along with 11-plus hours of battery life (on the FHD model), the Envy 13 has a sleek, ultraportable chassis, fast performance, and powerful speakers. Best of all, the Envy 13 starts at a reasonable $799, which is hundreds less than the competition. In many ways, the Envy 13 is what we wanted the new MacBook Air to be. The new HP Envy 13 is everything I was hoping the new MacBook Air would be: fast, attractive and affordable. Just be sure to buy the right model. We strongly recommend the 1080p version over the 4K model because it lasts several hours longer on a charge and costs less. In fact, if we were reviewing the 4K model separately, we'd only give it a 3.5 rating. You should also consider the Envy 13 with a 10th Gen CPU, although we haven't gotten the chance to review it yet. If you absolutely need a high-res display, the 4K Envy 13 is one of many good options. We also recommend the Samsung Notebook 9 Pro, which has a similarly premium design but much better battery life than the 4K Envy. The Microsoft Surface Laptop 2 is another recommended alternative, though you might want to wait a few months for the rumored Surface Laptop 3. Overall, the HP Envy 13 is a fantastic laptop that checks all the right boxes --- as long as you buy the 1080p model. Credit: Laptop Mag HP Envy 13 (2019) Specs BluetoothBluetooth 5.0 BrandHP CPUIntel Core i7-8565U Card SlotsmicroSD Company Websitehttps://www8.hp.com/us/en/home.html Display Size13.3 Graphics CardNvidia GeForce MX250 Hard Drive Size512GB Hard Drive TypePCIe NVMe M.2 Highest Available Resolution3840 x 2160 Native Resolution3840 x 2160 Operating SystemWindows 10 Home Ports (excluding USB)USB 3.1 with Type-C, USB 3.1 Always-On, USB 3.1, Headphone/Mic, microSD RAM16GB RAM Upgradable to16GB Size12.1 x 8.3 x .57 inches Touchpad Size4.3 x 2.2 inches USB Ports3 Video Memory2GB Warranty/Supportone-year warranty. Weight2.8 pounds Wi-Fi802.11ac Wi-Fi ModelIntel Wireless-AC 9560 ", - id="doc3" - ) + TextDocument(text="abc Graphics Surprise, surprise -- our 4K ", id="doc1"), + TextDocument(text="Short text about battery life and charging speed.", id="doc2"), ], - language="en" + language="en", ) - - output = await client.get_answers_from_text(params) + async with client: + output = await client.get_answers_from_text(params) assert output.answers for answer in output.answers: assert answer.answer - assert answer.confidence - assert answer.id - assert answer.offset - assert answer.length - assert answer.short_answer - assert answer.short_answer.text - assert answer.short_answer.confidence - assert answer.short_answer.offset is not None - assert answer.short_answer.length + assert answer.confidence is not None + assert answer.id is not None @pytest.mark.asyncio - async def test_query_text_with_dictparams(self, recorded_test, qna_creds): + async def test_query_text_with_str_records(self, recorded_test, qna_creds): client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) params = { "question": "How long it takes to charge surface?", + # Use object form matching TextDocument wire schema (records list of objects with text/id) "records": [ { - "text": "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - "id": "1" + "text": "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully.", + "id": "r1", }, { - "text": "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. "+ - "The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface.", - "id": "2" - } + "text": "You can use the USB port on your Surface Pro 4 power supply to charge other devices.", + "id": "r2", + }, ], - "language": "en" + "language": "en", } - async with client: output = await client.get_answers_from_text(params) - assert len(output.answers) == 3 - confident_answers = [a for a in output.answers if a.confidence > 0.9] - assert len(confident_answers) == 2 - assert confident_answers[0].short_answer.text == "two to four hours" + assert output.answers @pytest.mark.asyncio - async def test_query_text_with_str_records(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - params = { - "question": "How long it takes to charge surface?", - "records": [ - "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. "+ - "The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface.", - ], - "language": "en" - } - - async with client: - output = await client.get_answers_from_text(params) - assert len(output.answers) == 3 - confident_answers = [a for a in output.answers if a.confidence > 0.9] - assert len(confident_answers) == 2 - assert confident_answers[0].short_answer.text == "two to four hours" + async def test_query_text_overload_errors(self): # negative parameter validation + async with QuestionAnsweringClient("http://fake.com", AzureKeyCredential("123")) as client: + with pytest.raises(TypeError): + await client.get_answers_from_text("positional_one", "positional_two") # type: ignore[arg-type] + with pytest.raises(TypeError): + await client.get_answers_from_text("positional_options_bag", options="options bag by name") # type: ignore[arg-type] + params = AnswersFromTextOptions( + question="Meaning?", + text_documents=[TextDocument(text="foo", id="doc1"), TextDocument(text="bar", id="doc2")], + ) + with pytest.raises(TypeError): + await client.get_answers_from_text(options=params) # type: ignore[arg-type] + with pytest.raises(TypeError): + await client.get_answers_from_text(question="why?", text_documents=["foo", "bar"], options=params) # type: ignore[arg-type] + with pytest.raises(TypeError): + await client.get_answers_from_text(params, question="Why?") # type: ignore[arg-type] @pytest.mark.asyncio - async def test_query_text_overload(self, recorded_test, qna_creds): - client = QuestionAnsweringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - + async def test_query_text_default_lang_override(self, recorded_test, qna_creds): + client = QuestionAnsweringClient( + qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"]), default_language="es" + ) async with client: - with pytest.raises(TypeError): - await client.get_answers_from_text( - question="How long it takes to charge surface?", - text_documents=[ - "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", + output = await client.get_answers_from_text( + { + "question": "How long it takes to charge surface?", + # explicit language to assert value (client default_language does not auto inject into body) + "language": "es", + # normalize field name for clarity + "records": [ { - "text": "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. "+ - "The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface.", - "id": "2" + "text": "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully.", + "id": "doc1", } - ] - ) + ], + }, + raw_response_hook=lambda r: _assert_request_language(r, "es"), + ) output = await client.get_answers_from_text( - question="How long it takes to charge surface?", - text_documents=[ - "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. " + - "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. "+ - "The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface.", - ] + { + "question": "How long it takes to charge surface?", + "language": "en", + "records": [ + { + "text": "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully.", + "id": "doc1", + } + ], + }, + raw_response_hook=lambda r: _assert_request_language(r, "en"), ) - assert len(output.answers) == 3 - confident_answers = [a for a in output.answers if a.confidence > 0.9] - assert len(confident_answers) == 2 - assert confident_answers[0].short_answer.text == "two to four hours" + assert output is not None - @pytest.mark.asyncio - async def test_query_text_overload_positional_and_kwarg(self): - async with QuestionAnsweringClient("http://fake.com", AzureKeyCredential("123")) as client: - with pytest.raises(TypeError): - await client.get_answers_from_text("positional_one", "positional_two") - with pytest.raises(TypeError): - await client.get_answers_from_text("positional_options_bag", options="options bag by name") + +def _assert_request_language(response, expected): + import json + + body = json.loads(response.http_request.content) + assert body.get("language") == expected diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_update_knowledge_sources.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_update_knowledge_sources.py deleted file mode 100644 index a7320a71e466..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_update_knowledge_sources.py +++ /dev/null @@ -1,123 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest -from azure.ai.language.questionanswering.authoring import AuthoringClient -from azure.core.credentials import AzureKeyCredential - -from helpers import QnaAuthoringHelper -from testcase import QuestionAnsweringTestCase - - -class TestSourcesQnasSynonyms(QuestionAnsweringTestCase): - - @pytest.mark.live_test_only("Needs re-recording to work with new common sanitizers") - def test_add_source(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - QnaAuthoringHelper.create_test_project(client, project_name=project_name, **self.kwargs_for_polling) - - # add sources - source_display_name = "MicrosoftFAQ" - sources_poller = client.begin_update_sources( - project_name=project_name, - sources=[{ - "op": "add", - "value": { - "displayName": source_display_name, - "source": "https://www.microsoft.com/en-in/software-download/faq", - "sourceUri": "https://www.microsoft.com/en-in/software-download/faq", - "sourceKind": "url", - "contentStructureKind": "unstructured", - "refresh": False - } - }], - **self.kwargs_for_polling - ) - sources = sources_poller.result() # wait until done - for source in sources: - assert source["sourceKind"] - - # assert - sources = client.list_sources( - project_name=project_name - ) - source_added = False - for s in sources: - if ("displayName" in s) and s["displayName"] == source_display_name: - source_added = True - assert source_added - - def test_add_qna(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - QnaAuthoringHelper.create_test_project(client, project_name=project_name, **self.kwargs_for_polling) - - # add qnas - question = "What is the easiest way to use azure services in my .NET project?" - answer = "Using Microsoft's Azure SDKs" - qna_poller = client.begin_update_qnas( - project_name=project_name, - qnas=[{ - "op": "add", - "value": { - "questions": [ - question - ], - "answer": answer - } - }], - **self.kwargs_for_polling - ) - qnas = qna_poller.result() - for qna in qnas: - assert qna["questions"] - assert qna["answer"] - - # assert - qnas = client.list_qnas( - project_name=project_name - ) - qna_added = False - for qna in qnas: - if ("answer" in qna and "questions" in qna) and (qna["answer"] == answer and question in qna["questions"]): - qna_added = True - assert qna_added - - def test_add_synonym(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - QnaAuthoringHelper.create_test_project(client, project_name=project_name, **self.kwargs_for_polling) - - # add synonyms - client.update_synonyms( - project_name=project_name, - synonyms={ - "value": [ - { - "alterations": [ - "qnamaker", - "qna maker" - ] - } - ] - } - ) - - # assert - synonym_added = False - synonyms = client.list_synonyms( - project_name=project_name - ) - for s in synonyms: - if ("alterations" in s) and ("qnamaker" in s["alterations"] and "qna maker" in s["alterations"]): - synonym_added = True - assert synonym_added diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_update_knowledge_sources_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_update_knowledge_sources_async.py deleted file mode 100644 index 4b6373afbcf4..000000000000 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/test_update_knowledge_sources_async.py +++ /dev/null @@ -1,127 +0,0 @@ -# coding=utf-8 -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest - -from azure.ai.language.questionanswering.authoring.aio import AuthoringClient -from azure.core.credentials import AzureKeyCredential - -from helpers import QnaAuthoringAsyncHelper -from testcase import QuestionAnsweringTestCase - - -class TestSourcesQnasSynonymsAsync(QuestionAnsweringTestCase): - - @pytest.mark.live_test_only("Needs re-recording to work with new common sanitizers") - @pytest.mark.asyncio - async def test_add_source(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - await QnaAuthoringAsyncHelper.create_test_project(client, project_name=project_name, **self.kwargs_for_polling) - - # add sources - source_display_name = "MicrosoftFAQ" - sources_poller = await client.begin_update_sources( - project_name=project_name, - sources=[{ - "op": "add", - "value": { - "displayName": source_display_name, - "source": "https://www.microsoft.com/en-in/software-download/faq", - "sourceUri": "https://www.microsoft.com/en-in/software-download/faq", - "sourceKind": "url", - "contentStructureKind": "unstructured", - "refresh": False - } - }], - **self.kwargs_for_polling - ) - sources = await sources_poller.result() # wait until done - async for source in sources: - assert source["sourceKind"] - - # assert - sources = client.list_sources( - project_name=project_name - ) - source_added = False - async for s in sources: - if ("displayName" in s) and s["displayName"] == source_display_name: - source_added = True - assert source_added - - @pytest.mark.asyncio - async def test_add_qna(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - await QnaAuthoringAsyncHelper.create_test_project(client, project_name=project_name, **self.kwargs_for_polling) - - # add qnas - question = "What is the easiest way to use azure services in my .NET project?" - answer = "Using Microsoft's Azure SDKs" - qna_poller = await client.begin_update_qnas( - project_name=project_name, - qnas=[{ - "op": "add", - "value": { - "questions": [ - question - ], - "answer": answer - } - }], - **self.kwargs_for_polling - ) - qnas = await qna_poller.result() - async for qna in qnas: - assert qna["questions"] - assert qna["answer"] - - # assert - qnas = client.list_qnas( - project_name=project_name - ) - qna_added = False - async for qna in qnas: - if ("answer" in qna and "questions" in qna) and (qna["answer"] == answer and question in qna["questions"]): - qna_added = True - assert qna_added - - @pytest.mark.asyncio - async def test_add_synonym(self, recorded_test, qna_creds): - client = AuthoringClient(qna_creds["qna_endpoint"], AzureKeyCredential(qna_creds["qna_key"])) - - # create project - project_name = "IssacNewton" - await QnaAuthoringAsyncHelper.create_test_project(client, project_name=project_name, **self.kwargs_for_polling) - - # add synonyms - await client.update_synonyms( - project_name=project_name, - synonyms={ - "value": [ - { - "alterations": [ - "qnamaker", - "qna maker" - ] - } - ] - } - ) - - # assert - synonym_added = False - synonyms = client.list_synonyms( - project_name=project_name - ) - async for s in synonyms: - if ("alterations" in s) and ("qnamaker" in s["alterations"] and "qna maker" in s["alterations"]): - synonym_added = True - assert synonym_added diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/testcase.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/testcase.py index 611ff8ba3d25..965f9e721089 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/testcase.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tests/testcase.py @@ -1,15 +1,11 @@ - # coding: utf-8 # ------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -------------------------------------------------------------------------- +# Base test case for runtime Question Answering tests +# ------------------------------------------------------------------------- from devtools_testutils import AzureRecordedTestCase class QuestionAnsweringTestCase(AzureRecordedTestCase): - @property def kwargs_for_polling(self): if self.is_playback: diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering/tsp-location.yaml b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tsp-location.yaml new file mode 100644 index 000000000000..25183342e677 --- /dev/null +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering/tsp-location.yaml @@ -0,0 +1,5 @@ +directory: specification/cognitiveservices/data-plane/LanguageQuestionAnswering +commit: 5e36c7f1e3e8710fcaa0e7ae44d8742d9a91e665 +additionalDirectories: [] +repo: Azure/azure-rest-api-specs +