-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Split Text Analytics to function based package private clients #7050
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
16da16e
ab29f4c
8af42a8
6095719
c1099d4
f094d1f
b2f32fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| package com.azure.ai.textanalytics; | ||
|
|
||
| import com.azure.ai.textanalytics.implementation.TextAnalyticsClientImpl; | ||
| import com.azure.ai.textanalytics.implementation.models.DocumentError; | ||
| import com.azure.ai.textanalytics.implementation.models.DocumentSentiment; | ||
| import com.azure.ai.textanalytics.implementation.models.MultiLanguageBatchInput; | ||
| import com.azure.ai.textanalytics.implementation.models.SentimentConfidenceScorePerLabel; | ||
| import com.azure.ai.textanalytics.implementation.models.SentimentResponse; | ||
| import com.azure.ai.textanalytics.models.AnalyzeSentimentResult; | ||
| import com.azure.ai.textanalytics.models.DocumentResultCollection; | ||
| import com.azure.ai.textanalytics.models.TextAnalyticsRequestOptions; | ||
| import com.azure.ai.textanalytics.models.TextDocumentInput; | ||
| import com.azure.ai.textanalytics.models.TextSentiment; | ||
| import com.azure.ai.textanalytics.models.TextSentimentClass; | ||
| import com.azure.core.http.rest.Response; | ||
| import com.azure.core.http.rest.SimpleResponse; | ||
| import com.azure.core.util.Context; | ||
| import com.azure.core.util.logging.ClientLogger; | ||
| import reactor.core.publisher.Mono; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import static com.azure.ai.textanalytics.Transforms.mapByIndex; | ||
| import static com.azure.ai.textanalytics.Transforms.toBatchStatistics; | ||
| import static com.azure.ai.textanalytics.Transforms.toMultiLanguageInput; | ||
| import static com.azure.ai.textanalytics.Transforms.toTextAnalyticsError; | ||
| import static com.azure.ai.textanalytics.Transforms.toTextDocumentStatistics; | ||
|
|
||
| /** | ||
| * Helper class for managing sentiment analysis endpoint. | ||
| */ | ||
| class AnalyzeSentimentClient { | ||
samvaity marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| private final ClientLogger logger; | ||
| private final TextAnalyticsClientImpl service; | ||
|
|
||
| /** | ||
| * Create a {@code AnalyzeSentimentClient} that sends requests to the Text Analytics services's sentiment analysis | ||
| * endpoint. | ||
| * | ||
| * @param service The proxy service used to perform REST calls. | ||
| * @param logger The logger for the {@link TextAnalyticsAsyncClient} class. | ||
| */ | ||
| AnalyzeSentimentClient(TextAnalyticsClientImpl service, ClientLogger logger) { | ||
samvaity marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| this.logger = logger; | ||
| this.service = service; | ||
| } | ||
|
|
||
| Mono<Response<AnalyzeSentimentResult>> analyzeSentimentWithResponse(String text, String language, Context context) { | ||
| Objects.requireNonNull(text, "'text' cannot be null."); | ||
|
|
||
| return analyzeBatchSentimentWithResponse( | ||
| Collections.singletonList(new TextDocumentInput("0", text, language)), null, context) | ||
| .map(response -> new SimpleResponse<>(response, response.getValue().iterator().next())); | ||
| } | ||
|
|
||
| Mono<Response<DocumentResultCollection<AnalyzeSentimentResult>>> analyzeSentimentWithResponse( | ||
| List<String> textInputs, String language, Context context) { | ||
| Objects.requireNonNull(textInputs, "'textInputs' cannot be null."); | ||
|
|
||
| List<TextDocumentInput> documentInputs = mapByIndex(textInputs, (index, value) -> | ||
| new TextDocumentInput(index, value, language)); | ||
| return analyzeBatchSentimentWithResponse(documentInputs, null, context); | ||
| } | ||
|
|
||
| Mono<Response<DocumentResultCollection<AnalyzeSentimentResult>>> analyzeBatchSentimentWithResponse( | ||
| List<TextDocumentInput> textInputs, TextAnalyticsRequestOptions options, Context context) { | ||
| Objects.requireNonNull(textInputs, "'textInputs' cannot be null."); | ||
|
|
||
| final MultiLanguageBatchInput batchInput = new MultiLanguageBatchInput() | ||
| .setDocuments(toMultiLanguageInput(textInputs)); | ||
| return service.sentimentWithRestResponseAsync( | ||
| batchInput, | ||
| options == null ? null : options.getModelVersion(), | ||
| options == null ? null : options.showStatistics(), context) | ||
| .doOnSubscribe(ignoredValue -> logger.info("A batch of text sentiment input - {}", textInputs.toString())) | ||
| .doOnSuccess(response -> logger.info("A batch of text sentiment output - {}", response)) | ||
| .doOnError(error -> logger.warning("Failed to text sentiment - {}", error)) | ||
| .map(response -> new SimpleResponse<>(response, toDocumentResultCollection(response.getValue()))); | ||
| } | ||
|
|
||
| /** | ||
| * Helper method to convert the service response of {@link SentimentResponse} to {@link DocumentResultCollection}. | ||
| * | ||
| * @param sentimentResponse the {@link SentimentResponse} returned by the service. | ||
| * | ||
| * @return the {@link DocumentResultCollection} of {@link AnalyzeSentimentResult} to be returned by the SDK. | ||
| */ | ||
| private DocumentResultCollection<AnalyzeSentimentResult> toDocumentResultCollection( | ||
| final SentimentResponse sentimentResponse) { | ||
| List<AnalyzeSentimentResult> analyzeSentimentResults = new ArrayList<>(); | ||
| for (DocumentSentiment documentSentiment : sentimentResponse.getDocuments()) { | ||
| analyzeSentimentResults.add(convertToTextSentimentResult(documentSentiment)); | ||
| } | ||
| for (DocumentError documentError : sentimentResponse.getErrors()) { | ||
| final com.azure.ai.textanalytics.models.TextAnalyticsError error = | ||
| toTextAnalyticsError(documentError.getError()); | ||
| analyzeSentimentResults.add(new AnalyzeSentimentResult(documentError.getId(), null, | ||
| error, null, null)); | ||
| } | ||
| return new DocumentResultCollection<>(analyzeSentimentResults, | ||
| sentimentResponse.getModelVersion(), sentimentResponse.getStatistics() == null ? null | ||
| : toBatchStatistics(sentimentResponse.getStatistics())); | ||
| } | ||
|
|
||
| /** | ||
| * Helper method to convert the service response of {@link DocumentSentiment} to {@link AnalyzeSentimentResult}. | ||
| * | ||
| * @param documentSentiment the {@link DocumentSentiment} returned by the service. | ||
| * | ||
| * @return the {@link AnalyzeSentimentResult} to be returned by the SDK. | ||
| */ | ||
| private AnalyzeSentimentResult convertToTextSentimentResult(final DocumentSentiment documentSentiment) { | ||
| // Document text sentiment | ||
| final TextSentimentClass documentSentimentClass = TextSentimentClass.fromString(documentSentiment. | ||
| getSentiment().toString()); | ||
| if (documentSentimentClass == null) { | ||
| // Not throw exception for an invalid Sentiment type because we should not skip processing the | ||
| // other response. It is a service issue. | ||
| logger.logExceptionAsWarning( | ||
| new RuntimeException(String.format("'%s' is not valid text sentiment.", | ||
| documentSentiment.getSentiment()))); | ||
| } | ||
| final SentimentConfidenceScorePerLabel confidenceScorePerLabel = documentSentiment.getDocumentScores(); | ||
|
|
||
| // Sentence text sentiment | ||
| final List<TextSentiment> sentenceSentimentTexts = documentSentiment.getSentences().stream() | ||
| .map(sentenceSentiment -> { | ||
| TextSentimentClass sentimentClass = TextSentimentClass.fromString(sentenceSentiment | ||
| .getSentiment().toString()); | ||
| if (sentimentClass == null) { | ||
| // Not throw exception for an invalid Sentiment type because we should not skip processing the | ||
| // other response. It is a service issue. | ||
| logger.logExceptionAsWarning( | ||
| new RuntimeException(String.format("'%s' is not valid text sentiment.", | ||
| sentenceSentiment.getSentiment()))); | ||
| } | ||
| SentimentConfidenceScorePerLabel confidenceScorePerSentence = sentenceSentiment.getSentenceScores(); | ||
|
|
||
| return new TextSentiment(sentimentClass, confidenceScorePerSentence.getNegative(), | ||
| confidenceScorePerSentence.getNeutral(), confidenceScorePerSentence.getPositive(), | ||
| sentenceSentiment.getLength(), sentenceSentiment.getOffset()); | ||
|
|
||
| }).collect(Collectors.toList()); | ||
|
|
||
| return new AnalyzeSentimentResult(documentSentiment.getId(), | ||
| documentSentiment.getStatistics() == null ? null | ||
| : toTextDocumentStatistics(documentSentiment.getStatistics()), null, | ||
| new TextSentiment(documentSentimentClass, confidenceScorePerLabel.getNegative(), | ||
| confidenceScorePerLabel.getNeutral(), confidenceScorePerLabel.getPositive(), | ||
| sentenceSentimentTexts.stream().mapToInt(TextSentiment::getLength).sum(), 0), | ||
| sentenceSentimentTexts); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| package com.azure.ai.textanalytics; | ||
|
|
||
| import com.azure.ai.textanalytics.implementation.TextAnalyticsClientImpl; | ||
| import com.azure.ai.textanalytics.implementation.models.DocumentError; | ||
| import com.azure.ai.textanalytics.implementation.models.DocumentLanguage; | ||
| import com.azure.ai.textanalytics.implementation.models.LanguageBatchInput; | ||
| import com.azure.ai.textanalytics.implementation.models.LanguageInput; | ||
| import com.azure.ai.textanalytics.implementation.models.LanguageResult; | ||
| import com.azure.ai.textanalytics.models.DetectLanguageInput; | ||
| import com.azure.ai.textanalytics.models.DetectLanguageResult; | ||
| import com.azure.ai.textanalytics.models.DetectedLanguage; | ||
| import com.azure.ai.textanalytics.models.DocumentResultCollection; | ||
| import com.azure.ai.textanalytics.models.TextAnalyticsRequestOptions; | ||
| import com.azure.core.http.rest.Response; | ||
| import com.azure.core.http.rest.SimpleResponse; | ||
| import com.azure.core.util.Context; | ||
| import com.azure.core.util.logging.ClientLogger; | ||
| import reactor.core.publisher.Mono; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import static com.azure.ai.textanalytics.Transforms.mapByIndex; | ||
|
|
||
| /** | ||
| * Helper class for managing detect language endpoint. | ||
| */ | ||
| class DetectLanguageClient { | ||
samvaity marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| private final ClientLogger logger; | ||
| private final TextAnalyticsClientImpl service; | ||
|
|
||
| /** | ||
| * Create a {@code DetectLanguageClient} that sends requests to the Text Analytics services's detect language | ||
| * endpoint. | ||
| * | ||
| * @param service The proxy service used to perform REST calls. | ||
| * @param logger The logger for the {@link TextAnalyticsAsyncClient} class. | ||
| */ | ||
| DetectLanguageClient(TextAnalyticsClientImpl service, ClientLogger logger) { | ||
samvaity marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| this.logger = logger; | ||
| this.service = service; | ||
| } | ||
|
|
||
| Mono<Response<DetectLanguageResult>> detectLanguageWithResponse(String text, String countryHint, Context context) { | ||
| Objects.requireNonNull(text, "'text' cannot be null."); | ||
| List<DetectLanguageInput> languageInputs = Collections.singletonList(new DetectLanguageInput("0", | ||
| text, countryHint)); | ||
| return detectBatchLanguagesWithResponse(languageInputs, null, context).map(response -> | ||
| new SimpleResponse<>(response, response.getValue().iterator().next())); | ||
| } | ||
|
|
||
| Mono<Response<DocumentResultCollection<DetectLanguageResult>>> detectLanguagesWithResponse(List<String> textInputs, | ||
| String countryHint, Context context) { | ||
| Objects.requireNonNull(textInputs, "'textInputs' cannot be null."); | ||
| List<DetectLanguageInput> detectLanguageInputs = mapByIndex(textInputs, (index, value) -> | ||
| new DetectLanguageInput(index, value, countryHint)); | ||
|
|
||
| return detectBatchLanguagesWithResponse(detectLanguageInputs, null, context); | ||
| } | ||
|
|
||
| Mono<Response<DocumentResultCollection<DetectLanguageResult>>> detectBatchLanguagesWithResponse( | ||
| List<DetectLanguageInput> textInputs, TextAnalyticsRequestOptions options, Context context) { | ||
| Objects.requireNonNull(textInputs, "'textInputs' cannot be null."); | ||
|
|
||
| final LanguageBatchInput languageBatchInput = new LanguageBatchInput() | ||
| .setDocuments(textInputs.stream().map(detectLanguageInput -> new LanguageInput() | ||
| .setId(detectLanguageInput.getId()).setText(detectLanguageInput.getText()) | ||
| .setCountryHint(detectLanguageInput.getCountryHint())).collect(Collectors.toList())); | ||
|
|
||
| return service.languagesWithRestResponseAsync( | ||
| languageBatchInput, options == null ? null : options.getModelVersion(), | ||
| options == null ? null : options.showStatistics(), context) | ||
| .doOnSubscribe(ignoredValue -> logger.info("A batch of language input - {}", textInputs.toString())) | ||
| .doOnSuccess(response -> logger.info("A batch of detected language output - {}", response.getValue())) | ||
| .doOnError(error -> logger.warning("Failed to detected languages - {}", error)) | ||
| .map(response -> new SimpleResponse<>(response, toDocumentResultCollection(response.getValue()))); | ||
| } | ||
|
|
||
| /** | ||
| * Helper method to convert the service response of {@link LanguageResult} to {@link DocumentResultCollection}. | ||
| * | ||
| * @param languageResult the {@link LanguageResult} returned by the service. | ||
| * | ||
| * @return the {@link DocumentResultCollection} of {@link DetectLanguageResult} to be returned by the SDK. | ||
| */ | ||
| private DocumentResultCollection<DetectLanguageResult> toDocumentResultCollection( | ||
| final LanguageResult languageResult) { | ||
|
|
||
| final List<DetectLanguageResult> detectLanguageResults = new ArrayList<>(); | ||
| for (DocumentLanguage documentLanguage : languageResult.getDocuments()) { | ||
| DetectedLanguage primaryLanguage = null; | ||
| if (documentLanguage.getDetectedLanguages().size() >= 1) { | ||
| com.azure.ai.textanalytics.implementation.models.DetectedLanguage detectedLanguageResult = | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's confusing to have the same name for implementation and public model classes.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Models in the implementation folder are auto-generated models. Some of the public model classes adopt the name but they are different. We want to explore the same name as |
||
| documentLanguage.getDetectedLanguages().get(0); | ||
| primaryLanguage = new DetectedLanguage(detectedLanguageResult.getName(), | ||
| detectedLanguageResult.getIso6391Name(), detectedLanguageResult.getScore()); | ||
| } | ||
| detectLanguageResults.add(new DetectLanguageResult(documentLanguage.getId(), | ||
| documentLanguage.getStatistics() == null | ||
| ? null : Transforms.toTextDocumentStatistics(documentLanguage.getStatistics()), | ||
| null, | ||
| primaryLanguage, | ||
| documentLanguage.getDetectedLanguages().stream().map(detectedLanguage -> | ||
| new DetectedLanguage(detectedLanguage.getName(), detectedLanguage.getIso6391Name(), | ||
| detectedLanguage.getScore())).collect(Collectors.toList()))); | ||
| } | ||
|
|
||
| for (DocumentError documentError : languageResult.getErrors()) { | ||
| com.azure.ai.textanalytics.models.TextAnalyticsError error = | ||
| Transforms.toTextAnalyticsError(documentError.getError()); | ||
| detectLanguageResults.add( | ||
| new DetectLanguageResult(documentError.getId(), null, error, null, null)); | ||
| } | ||
|
|
||
| return new DocumentResultCollection<>(detectLanguageResults, languageResult.getModelVersion(), | ||
| languageResult.getStatistics() == null ? null | ||
| : Transforms.toBatchStatistics(languageResult.getStatistics())); | ||
| } | ||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.