From de1867d1a263ea4641860e54577ca9799983a961 Mon Sep 17 00:00:00 2001 From: tracyboehrer Date: Tue, 23 Feb 2021 13:45:41 -0600 Subject: [PATCH] Minor QnA test issues and formatting (#1016) * Some tests weren't passing, just on some machines. * Standardized Okhttp3 version across modules * Okhttp3 dependencies in parent pom * Code reformatting (no logic changes) * Corrected a few CompletableFuture exceptional return results. --- libraries/bot-ai-qna/pom.xml | 23 +- .../com/microsoft/bot/ai/qna/QnAMaker.java | 104 +++++---- .../microsoft/bot/ai/qna/QnAMakerClient.java | 16 +- .../bot/ai/qna/QnAMakerRecognizer.java | 141 +++++++----- .../bot/ai/qna/TelemetryQnAMaker.java | 8 +- .../bot/ai/qna/dialogs/QnAMakerDialog.java | 205 +++++++++++------- .../bot/ai/qna/models/RankerTypes.java | 3 +- .../bot/ai/qna/utils/ActiveLearningUtils.java | 24 +- .../bot/ai/qna/utils/BindToActivity.java | 4 +- .../bot/ai/qna/utils/GenerateAnswerUtils.java | 101 ++++++--- .../bot/ai/qna/utils/HttpRequestUtils.java | 30 ++- .../bot/ai/qna/utils/QnACardBuilder.java | 3 +- .../ai/qna/utils/QnATelemetryConstants.java | 3 +- .../bot/ai/qna/utils/TrainUtils.java | 9 +- .../bot/ai/qna/QnAMakerRecognizerTests.java | 1 + .../microsoft/bot/ai/qna/QnAMakerTests.java | 12 +- libraries/bot-connector/pom.xml | 30 +-- pom.xml | 22 ++ 18 files changed, 460 insertions(+), 279 deletions(-) diff --git a/libraries/bot-ai-qna/pom.xml b/libraries/bot-ai-qna/pom.xml index 4de263b5..189aa41c 100644 --- a/libraries/bot-ai-qna/pom.xml +++ b/libraries/bot-ai-qna/pom.xml @@ -49,14 +49,20 @@ junit junit + + org.mockito + mockito-core + + org.slf4j slf4j-api + com.microsoft.bot - bot-builder - ${project.version} + bot-integration-spring + compile com.microsoft.bot @@ -69,27 +75,16 @@ test-jar test - - com.microsoft.bot - bot-integration-spring - 4.6.0-preview8 - compile - + com.squareup.okhttp3 okhttp - 4.9.0 com.squareup.okhttp3 mockwebserver - 4.9.0 test - - org.mockito - mockito-core - diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMaker.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMaker.java index b6b5d3d1..821cf019 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMaker.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMaker.java @@ -16,6 +16,7 @@ import com.microsoft.bot.ai.qna.utils.TrainUtils; import com.microsoft.bot.builder.BotTelemetryClient; import com.microsoft.bot.builder.NullBotTelemetryClient; import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.connector.Async; import com.microsoft.bot.restclient.serializer.JacksonAdapter; import com.microsoft.bot.schema.Activity; import com.microsoft.bot.schema.ActivityTypes; @@ -75,8 +76,12 @@ public class QnAMaker implements QnAMakerClient, TelemetryQnAMaker { * identifiable information in telemetry * events. */ - public QnAMaker(QnAMakerEndpoint withEndpoint, QnAMakerOptions options, - BotTelemetryClient withTelemetryClient, Boolean withLogPersonalInformation) { + public QnAMaker( + QnAMakerEndpoint withEndpoint, + QnAMakerOptions options, + BotTelemetryClient withTelemetryClient, + Boolean withLogPersonalInformation + ) { if (withLogPersonalInformation == null) { withLogPersonalInformation = false; } @@ -100,7 +105,8 @@ public class QnAMaker implements QnAMakerClient, TelemetryQnAMaker { if (this.endpoint.getHost().endsWith("v2.0") || this.endpoint.getHost().endsWith("v3.0")) { throw new UnsupportedOperationException( - "v2.0 and v3.0 of QnA Maker service" + " is no longer supported in the QnA Maker."); + "v2.0 and v3.0 of QnA Maker service" + " is no longer supported in the QnA Maker." + ); } this.telemetryClient = withTelemetryClient != null ? withTelemetryClient : new NullBotTelemetryClient(); @@ -113,8 +119,8 @@ public class QnAMaker implements QnAMakerClient, TelemetryQnAMaker { /** * Initializes a new instance of the {@link QnAMaker} class. * - * @param withEndpoint The endpoint of the knowledge base to query. - * @param options The options for the QnA Maker knowledge base. + * @param withEndpoint The endpoint of the knowledge base to query. + * @param options The options for the QnA Maker knowledge base. */ public QnAMaker(QnAMakerEndpoint withEndpoint, @Nullable QnAMakerOptions options) { this(withEndpoint, options, null, null); @@ -170,10 +176,14 @@ public class QnAMaker implements QnAMakerClient, TelemetryQnAMaker { * @return A list of answers for the user query, sorted in decreasing order of * ranking score. */ - public CompletableFuture getAnswers(TurnContext turnContext, QnAMakerOptions options, - Map telemetryProperties, @Nullable Map telemetryMetrics) { + public CompletableFuture getAnswers( + TurnContext turnContext, + QnAMakerOptions options, + Map telemetryProperties, + @Nullable Map telemetryMetrics + ) { return this.getAnswersRaw(turnContext, options, telemetryProperties, telemetryMetrics) - .thenApply(result -> result.getAnswers()); + .thenApply(result -> result.getAnswers()); } /** @@ -191,22 +201,29 @@ public class QnAMaker implements QnAMakerClient, TelemetryQnAMaker { * @return A list of answers for the user query, sorted in decreasing order of * ranking score. */ - public CompletableFuture getAnswersRaw(TurnContext turnContext, QnAMakerOptions options, - @Nullable Map telemetryProperties, @Nullable Map telemetryMetrics) { + public CompletableFuture getAnswersRaw( + TurnContext turnContext, + QnAMakerOptions options, + @Nullable Map telemetryProperties, + @Nullable Map telemetryMetrics + ) { if (turnContext == null) { - throw new IllegalArgumentException("turnContext"); + return Async.completeExceptionally(new IllegalArgumentException("turnContext")); } if (turnContext.getActivity() == null) { - throw new IllegalArgumentException( - String.format("The %1$s property for %2$s can't be null.", "Activity", "turnContext")); + return Async.completeExceptionally( + new IllegalArgumentException( + String.format("The %1$s property for %2$s can't be null.", "Activity", "turnContext") + ) + ); } Activity messageActivity = turnContext.getActivity(); if (messageActivity == null || !messageActivity.isType(ActivityTypes.MESSAGE)) { - throw new IllegalArgumentException("Activity type is not a message"); + return Async.completeExceptionally(new IllegalArgumentException("Activity type is not a message")); } if (Strings.isNullOrEmpty(turnContext.getActivity().getText())) { - throw new IllegalArgumentException("Null or empty text"); + return Async.completeExceptionally(new IllegalArgumentException("Null or empty text")); } return this.generateAnswerHelper.getAnswersRaw(turnContext, messageActivity, options).thenCompose(result -> { @@ -253,14 +270,19 @@ public class QnAMaker implements QnAMakerClient, TelemetryQnAMaker { * @return A Task representing the work to be executed. * @throws IOException Throws an IOException if there is any. */ - protected CompletableFuture onQnaResults(QueryResult[] queryResults, TurnContext turnContext, - @Nullable Map telemetryProperties, @Nullable Map telemetryMetrics) - throws IOException { - return fillQnAEvent(queryResults, turnContext, telemetryProperties, telemetryMetrics).thenAccept(eventData -> { - // Track the event - this.telemetryClient.trackEvent(QnATelemetryConstants.QNA_MSG_EVENT, eventData.getLeft(), - eventData.getRight()); - }); + protected CompletableFuture onQnaResults( + QueryResult[] queryResults, + TurnContext turnContext, + @Nullable Map telemetryProperties, + @Nullable Map telemetryMetrics + ) throws IOException { + return fillQnAEvent(queryResults, turnContext, telemetryProperties, telemetryMetrics).thenAccept( + eventData -> { + // Track the event + this.telemetryClient + .trackEvent(QnATelemetryConstants.QNA_MSG_EVENT, eventData.getLeft(), eventData.getRight()); + } + ); } /** @@ -279,17 +301,20 @@ public class QnAMaker implements QnAMakerClient, TelemetryQnAMaker { * any properties passed from the GetAnswersAsync method. * @throws IOException Throws an IOException if there is any. */ - protected CompletableFuture, Map>> fillQnAEvent(QueryResult[] queryResults, - TurnContext turnContext, @Nullable Map telemetryProperties, - @Nullable Map telemetryMetrics) throws IOException { + protected CompletableFuture, Map>> fillQnAEvent( + QueryResult[] queryResults, + TurnContext turnContext, + @Nullable Map telemetryProperties, + @Nullable Map telemetryMetrics + ) throws IOException { Map properties = new HashMap(); Map metrics = new HashMap(); properties.put(QnATelemetryConstants.KNOWLEDGE_BASE_ID_PROPERTY, this.endpoint.getKnowledgeBaseId()); String text = turnContext.getActivity().getText(); - String userName = turnContext.getActivity().getFrom() != null - ? turnContext.getActivity().getFrom().getName() : null; + String userName = + turnContext.getActivity().getFrom() != null ? turnContext.getActivity().getFrom().getName() : null; // Use the LogPersonalInformation flag to toggle logging PII data, text and user // name are common examples @@ -307,9 +332,14 @@ public class QnAMaker implements QnAMakerClient, TelemetryQnAMaker { if (queryResults.length > 0) { JacksonAdapter jacksonAdapter = new JacksonAdapter(); QueryResult queryResult = queryResults[0]; - properties.put(QnATelemetryConstants.MATCHED_QUESTION_PROPERTY, - jacksonAdapter.serialize(queryResult.getQuestions())); - properties.put(QnATelemetryConstants.QUESTION_ID_PROPERTY, queryResult.getId().toString()); + properties.put( + QnATelemetryConstants.MATCHED_QUESTION_PROPERTY, + jacksonAdapter.serialize(queryResult.getQuestions()) + ); + properties.put( + QnATelemetryConstants.QUESTION_ID_PROPERTY, + queryResult.getId() != null ? queryResult.getId().toString() : "" + ); properties.put(QnATelemetryConstants.ANSWER_PROPERTY, queryResult.getAnswer()); metrics.put(QnATelemetryConstants.SCORE_PROPERTY, queryResult.getScore().doubleValue()); properties.put(QnATelemetryConstants.ARTICLE_FOUND_PROPERTY, "true"); @@ -323,13 +353,13 @@ public class QnAMaker implements QnAMakerClient, TelemetryQnAMaker { // Additional Properties can override "stock" properties. if (telemetryProperties != null) { Multimap multiMapTelemetryProperties = LinkedListMultimap.create(); - for (Entry entry: telemetryProperties.entrySet()) { + for (Entry entry : telemetryProperties.entrySet()) { multiMapTelemetryProperties.put(entry.getKey(), entry.getValue()); } - for (Entry entry: properties.entrySet()) { + for (Entry entry : properties.entrySet()) { multiMapTelemetryProperties.put(entry.getKey(), entry.getValue()); } - for (Entry> entry: multiMapTelemetryProperties.asMap().entrySet()) { + for (Entry> entry : multiMapTelemetryProperties.asMap().entrySet()) { telemetryProperties.put(entry.getKey(), entry.getValue().iterator().next()); } } @@ -337,13 +367,13 @@ public class QnAMaker implements QnAMakerClient, TelemetryQnAMaker { // Additional Metrics can override "stock" metrics. if (telemetryMetrics != null) { Multimap multiMapTelemetryMetrics = LinkedListMultimap.create(); - for (Entry entry: telemetryMetrics.entrySet()) { + for (Entry entry : telemetryMetrics.entrySet()) { multiMapTelemetryMetrics.put(entry.getKey(), entry.getValue()); } - for (Entry entry: metrics.entrySet()) { + for (Entry entry : metrics.entrySet()) { multiMapTelemetryMetrics.put(entry.getKey(), entry.getValue()); } - for (Entry> entry: multiMapTelemetryMetrics.asMap().entrySet()) { + for (Entry> entry : multiMapTelemetryMetrics.asMap().entrySet()) { telemetryMetrics.put(entry.getKey(), entry.getValue().iterator().next()); } } diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMakerClient.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMakerClient.java index 55da459d..e2299745 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMakerClient.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMakerClient.java @@ -33,8 +33,12 @@ public interface QnAMakerClient { * @return A list of answers for the user query, sorted in decreasing order of * ranking score. */ - CompletableFuture getAnswers(TurnContext turnContext, QnAMakerOptions options, - Map telemetryProperties, @Nullable Map telemetryMetrics); + CompletableFuture getAnswers( + TurnContext turnContext, + QnAMakerOptions options, + Map telemetryProperties, + @Nullable Map telemetryMetrics + ); /** * Generates an answer from the knowledge base. @@ -51,8 +55,12 @@ public interface QnAMakerClient { * @return A list of answers for the user query, sorted in decreasing order of * ranking score. */ - CompletableFuture getAnswersRaw(TurnContext turnContext, QnAMakerOptions options, - @Nullable Map telemetryProperties, @Nullable Map telemetryMetrics); + CompletableFuture getAnswersRaw( + TurnContext turnContext, + QnAMakerOptions options, + @Nullable Map telemetryProperties, + @Nullable Map telemetryMetrics + ); /** * Filters the ambiguous question for active learning. diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMakerRecognizer.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMakerRecognizer.java index b93e7f78..6c1ddc55 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMakerRecognizer.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/QnAMakerRecognizer.java @@ -197,8 +197,8 @@ public class QnAMakerRecognizer extends Recognizer { * Sets a value indicating whether gets or sets environment of knowledgebase to * be called. * - * @param withIsTest A value indicating whether to call test or prod environment of - * knowledgebase. + * @param withIsTest A value indicating whether to call test or prod environment + * of knowledgebase. */ public void setIsTest(Boolean withIsTest) { this.isTest = withIsTest; @@ -234,8 +234,8 @@ public class QnAMakerRecognizer extends Recognizer { /** * Sets {@link Metadata} join operator. * - * @param withStrictFiltersJoinOperator A value used for Join operation of Metadata - * {@link Metadata}. + * @param withStrictFiltersJoinOperator A value used for Join operation of + * Metadata {@link Metadata}. */ public void setStrictFiltersJoinOperator(JoinOperator withStrictFiltersJoinOperator) { this.strictFiltersJoinOperator = withStrictFiltersJoinOperator; @@ -290,7 +290,7 @@ public class QnAMakerRecognizer extends Recognizer { * Sets an expression to evaluate to set the context. * * @param withContext An expression to evaluate to QnARequestContext to pass as - * context. + * context. */ public void setContext(QnARequestContext withContext) { this.context = withContext; @@ -329,8 +329,8 @@ public class QnAMakerRecognizer extends Recognizer { * Sets the flag to determine if personal information should be logged in * telemetry. * - * @param withLogPersonalInformation The flag to indicate in personal information - * should be logged in telemetry. + * @param withLogPersonalInformation The flag to indicate in personal + * information should be logged in telemetry. */ public void setLogPersonalInformation(Boolean withLogPersonalInformation) { this.logPersonalInformation = withLogPersonalInformation; @@ -351,8 +351,12 @@ public class QnAMakerRecognizer extends Recognizer { * @return A {@link RecognizerResult} containing the QnA Maker result. */ @Override - public CompletableFuture recognize(DialogContext dialogContext, Activity activity, - Map telemetryProperties, Map telemetryMetrics) { + public CompletableFuture recognize( + DialogContext dialogContext, + Activity activity, + Map telemetryProperties, + Map telemetryMetrics + ) { // Identify matched intents RecognizerResult recognizerResult = new RecognizerResult(); recognizerResult.setText(activity.getText()); @@ -364,14 +368,11 @@ public class QnAMakerRecognizer extends Recognizer { List filters = new ArrayList(); // TODO this should be uncommented as soon as Expression is added in Java - /* if (this.includeDialogNameInMetadata.getValue(dialogContext.getState())) { - filters.add(new Metadata() { - { - setName("dialogName"); - setValue(dialogContext.getActiveDialog().getId()); - } - }); - } */ + /* + * if (this.includeDialogNameInMetadata.getValue(dialogContext.getState())) { + * filters.add(new Metadata() { { setName("dialogName"); + * setValue(dialogContext.getActiveDialog().getId()); } }); } + */ // if there is $qna.metadata set add to filters Metadata[] externalMetadata = this.metadata; @@ -402,13 +403,13 @@ public class QnAMakerRecognizer extends Recognizer { } Float internalTopAnswer = topAnswer.getScore(); if (topAnswer.getAnswer().trim().toUpperCase().startsWith(intentPrefix.toUpperCase())) { - recognizerResult.getIntents().put( - topAnswer.getAnswer().trim().substring(intentPrefix.length()).trim(), - new IntentScore() { - { - setScore(internalTopAnswer); - } - }); + recognizerResult.getIntents() + .put(topAnswer.getAnswer().trim().substring( + intentPrefix.length()).trim(), + new IntentScore() {{ + setScore(internalTopAnswer); + }} + ); } else { recognizerResult.getIntents().put(this.qnAMatchIntent, new IntentScore() { { @@ -440,9 +441,12 @@ public class QnAMakerRecognizer extends Recognizer { }); } - this.trackRecognizerResult(dialogContext, "QnAMakerRecognizerResult", this - .fillRecognizerResultTelemetryProperties(recognizerResult, telemetryProperties, dialogContext), - telemetryMetrics); + this.trackRecognizerResult( + dialogContext, + "QnAMakerRecognizerResult", + this.fillRecognizerResultTelemetryProperties(recognizerResult, telemetryProperties, dialogContext), + telemetryMetrics + ); return recognizerResult; }); }); @@ -473,8 +477,8 @@ public class QnAMakerRecognizer extends Recognizer { } }; - return CompletableFuture.completedFuture( - new QnAMaker(endpoint, new QnAMakerOptions(), this.getTelemetryClient(), logPersonalInfo)); + return CompletableFuture + .completedFuture(new QnAMaker(endpoint, new QnAMakerOptions(), this.getTelemetryClient(), logPersonalInfo)); } /** @@ -489,37 +493,53 @@ public class QnAMakerRecognizer extends Recognizer { * on the TelemetryClient. */ @Override - protected Map fillRecognizerResultTelemetryProperties(RecognizerResult recognizerResult, - Map telemetryProperties, @Nullable DialogContext dialogContext) { + protected Map fillRecognizerResultTelemetryProperties( + RecognizerResult recognizerResult, + Map telemetryProperties, + @Nullable DialogContext dialogContext + ) { if (dialogContext == null) { throw new IllegalArgumentException( - "dialogContext: DialogContext needed for state in " - + "AdaptiveRecognizer.FillRecognizerResultTelemetryProperties method."); + "dialogContext: DialogContext needed for state in " + + "AdaptiveRecognizer.FillRecognizerResultTelemetryProperties method." + ); } Map properties = new HashMap() { { - put("TopIntent", - !recognizerResult.getIntents().isEmpty() - ? (String) recognizerResult.getIntents().keySet().toArray()[0] - : null); - put("TopIntentScore", - !recognizerResult.getIntents().isEmpty() - ? Double.toString( - ((IntentScore) recognizerResult.getIntents().values().toArray()[0]).getScore()) - : null); - put("Intents", - !recognizerResult.getIntents().isEmpty() - ? Serialization.toStringSilent(recognizerResult.getIntents()) - : null); - put("Entities", - recognizerResult.getEntities() != null - ? Serialization.toStringSilent(recognizerResult.getEntities()) - : null); - put("AdditionalProperties", - !recognizerResult.getProperties().isEmpty() - ? Serialization.toStringSilent(recognizerResult.getProperties()) - : null); + put( + "TopIntent", + !recognizerResult.getIntents().isEmpty() + ? (String) recognizerResult.getIntents().keySet().toArray()[0] + : null + ); + put( + "TopIntentScore", + !recognizerResult.getIntents() + .isEmpty() + ? Double.toString( + ((IntentScore) recognizerResult.getIntents().values().toArray()[0]).getScore() + ) + : null + ); + put( + "Intents", + !recognizerResult.getIntents().isEmpty() + ? Serialization.toStringSilent(recognizerResult.getIntents()) + : null + ); + put( + "Entities", + recognizerResult.getEntities() != null + ? Serialization.toStringSilent(recognizerResult.getEntities()) + : null + ); + put( + "AdditionalProperties", + !recognizerResult.getProperties().isEmpty() + ? Serialization.toStringSilent(recognizerResult.getProperties()) + : null + ); } }; @@ -531,10 +551,15 @@ public class QnAMakerRecognizer extends Recognizer { // Additional Properties can override "stock properties". if (telemetryProperties != null) { telemetryProperties.putAll(properties); - Map> telemetryPropertiesMap = telemetryProperties.entrySet().stream().collect( - Collectors.groupingBy(Entry::getKey, Collectors.mapping(Entry::getValue, Collectors.toList()))); - return telemetryPropertiesMap.entrySet().stream() - .collect(Collectors.toMap(Entry::getKey, e -> e.getValue().get(0))); + Map> telemetryPropertiesMap = + telemetryProperties.entrySet() + .stream() + .collect( + Collectors.groupingBy(Entry::getKey, Collectors.mapping(Entry::getValue, Collectors.toList())) + ); + return telemetryPropertiesMap.entrySet() + .stream() + .collect(Collectors.toMap(Entry::getKey, e -> e.getValue().get(0))); } return properties; diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/TelemetryQnAMaker.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/TelemetryQnAMaker.java index 84152e76..3ad30ff8 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/TelemetryQnAMaker.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/TelemetryQnAMaker.java @@ -50,6 +50,10 @@ public interface TelemetryQnAMaker { * @return A list of answers for the user query, sorted in decreasing order of * ranking score. */ - CompletableFuture getAnswers(TurnContext turnContext, QnAMakerOptions options, - Map telemetryProperties, @Nullable Map telemetryMetrics); + CompletableFuture getAnswers( + TurnContext turnContext, + QnAMakerOptions options, + Map telemetryProperties, + @Nullable Map telemetryMetrics + ); } diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/QnAMakerDialog.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/QnAMakerDialog.java index 3a41b72c..6459b2d7 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/QnAMakerDialog.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/QnAMakerDialog.java @@ -3,6 +3,7 @@ package com.microsoft.bot.ai.qna.dialogs; +import com.microsoft.bot.connector.Async; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -81,8 +82,8 @@ public class QnAMakerDialog extends WaterfallDialog { private String cardNoMatchText; @JsonProperty("cardNoMatchResponse") - private BindToActivity cardNoMatchResponse = new BindToActivity( - MessageFactory.text(DEFAULT_CARD_NO_MATCH_RESPONSE)); + private BindToActivity cardNoMatchResponse = + new BindToActivity(MessageFactory.text(DEFAULT_CARD_NO_MATCH_RESPONSE)); @JsonProperty("strictFilters") private Metadata[] strictFilters; @@ -472,10 +473,20 @@ public class QnAMakerDialog extends WaterfallDialog { * default client. */ @SuppressWarnings("checkstyle:ParameterNumber") - public QnAMakerDialog(String dialogId, String withKnowledgeBaseId, String withEndpointKey, String withHostName, - @Nullable Activity withNoAnswer, Float withThreshold, String withActiveLearningCardTitle, - String withCardNoMatchText, Integer withTop, @Nullable Activity withCardNoMatchResponse, - @Nullable Metadata[] withStrictFilters, @Nullable OkHttpClient withHttpClient) { + public QnAMakerDialog( + String dialogId, + String withKnowledgeBaseId, + String withEndpointKey, + String withHostName, + @Nullable Activity withNoAnswer, + Float withThreshold, + String withActiveLearningCardTitle, + String withCardNoMatchText, + Integer withTop, + @Nullable Activity withCardNoMatchResponse, + @Nullable Metadata[] withStrictFilters, + @Nullable OkHttpClient withHttpClient + ) { super(dialogId, null); if (knowledgeBaseId == null) { throw new IllegalArgumentException("knowledgeBaseId"); @@ -491,14 +502,17 @@ public class QnAMakerDialog extends WaterfallDialog { this.endpointKey = withEndpointKey; this.threshold = withThreshold != null ? withThreshold : DEFAULT_THRESHOLD; this.top = withTop != null ? withTop : DEFAULT_TOP_N; - this.activeLearningCardTitle = withActiveLearningCardTitle != null ? withActiveLearningCardTitle - : DEFAULT_CARD_TITLE; + this.activeLearningCardTitle = + withActiveLearningCardTitle != null ? withActiveLearningCardTitle : DEFAULT_CARD_TITLE; this.cardNoMatchText = withCardNoMatchText != null ? withCardNoMatchText : DEFAULT_CARD_NO_MATCH_TEXT; this.strictFilters = withStrictFilters; - this.noAnswer = new BindToActivity( - withNoAnswer != null ? withNoAnswer : MessageFactory.text(DEFAULT_NO_ANSWER)); - this.cardNoMatchResponse = new BindToActivity(withCardNoMatchResponse != null ? withCardNoMatchResponse - : MessageFactory.text(DEFAULT_CARD_NO_MATCH_RESPONSE)); + this.noAnswer = + new BindToActivity(withNoAnswer != null ? withNoAnswer : MessageFactory.text(DEFAULT_NO_ANSWER)); + this.cardNoMatchResponse = new BindToActivity( + withCardNoMatchResponse != null + ? withCardNoMatchResponse + : MessageFactory.text(DEFAULT_CARD_NO_MATCH_RESPONSE) + ); this.httpClient = withHttpClient; // add waterfall steps @@ -542,14 +556,33 @@ public class QnAMakerDialog extends WaterfallDialog { * default client. */ @SuppressWarnings("checkstyle:ParameterNumber") - public QnAMakerDialog(String withKnowledgeBaseId, String withEndpointKey, String withHostName, - @Nullable Activity withNoAnswer, Float withThreshold, String withActiveLearningCardTitle, - String withCardNoMatchText, Integer withTop, @Nullable Activity withCardNoMatchResponse, - @Nullable Metadata[] withStrictFilters, - @Nullable OkHttpClient withHttpClient) { - this(QnAMakerDialog.class.getName(), withKnowledgeBaseId, withEndpointKey, withHostName, withNoAnswer, - withThreshold, withActiveLearningCardTitle, withCardNoMatchText, withTop, withCardNoMatchResponse, - withStrictFilters, withHttpClient); + public QnAMakerDialog( + String withKnowledgeBaseId, + String withEndpointKey, + String withHostName, + @Nullable Activity withNoAnswer, + Float withThreshold, + String withActiveLearningCardTitle, + String withCardNoMatchText, + Integer withTop, + @Nullable Activity withCardNoMatchResponse, + @Nullable Metadata[] withStrictFilters, + @Nullable OkHttpClient withHttpClient + ) { + this( + QnAMakerDialog.class.getName(), + withKnowledgeBaseId, + withEndpointKey, + withHostName, + withNoAnswer, + withThreshold, + withActiveLearningCardTitle, + withCardNoMatchText, + withTop, + withCardNoMatchResponse, + withStrictFilters, + withHttpClient + ); } /** @@ -570,7 +603,7 @@ public class QnAMakerDialog extends WaterfallDialog { @Override public CompletableFuture beginDialog(DialogContext dc, @Nullable Object options) { if (dc == null) { - throw new IllegalArgumentException("dc"); + return Async.completeExceptionally(new IllegalArgumentException("dc")); } if (!dc.getContext().getActivity().isType(ActivityTypes.MESSAGE)) { @@ -622,7 +655,7 @@ public class QnAMakerDialog extends WaterfallDialog { String reply = dc.getContext().getActivity().getText(); QnAMakerDialogOptions dialogOptions = (QnAMakerDialogOptions) ObjectPath - .getPathValue(dc.getActiveDialog().getState(), OPTIONS, QnAMakerDialogOptions.class); + .getPathValue(dc.getActiveDialog().getState(), OPTIONS, QnAMakerDialogOptions.class); if (reply.equalsIgnoreCase(dialogOptions.getResponseOptions().getCardNoMatchText())) { // it matches nomatch text, we like that. @@ -630,8 +663,10 @@ public class QnAMakerDialog extends WaterfallDialog { } List suggestedQuestions = (List) dc.getState().get("this.suggestedQuestions"); - if (suggestedQuestions != null && suggestedQuestions.stream() - .anyMatch(question -> question.compareToIgnoreCase(reply.trim()) == 0)) { + if ( + suggestedQuestions != null + && suggestedQuestions.stream().anyMatch(question -> question.compareToIgnoreCase(reply.trim()) == 0) + ) { // it matches one of the suggested actions, we like that. return CompletableFuture.completedFuture(true); } @@ -640,16 +675,17 @@ public class QnAMakerDialog extends WaterfallDialog { return this.getQnAMakerClient(dc).thenCompose(qnaClient -> { QnAMakerDialog.resetOptions(dc, dialogOptions); - return qnaClient.getAnswersRaw(dc.getContext(), dialogOptions.getQnAMakerOptions(), - null, null) - .thenApply(response -> { + return qnaClient.getAnswersRaw(dc.getContext(), dialogOptions.getQnAMakerOptions(), null, null) + .thenApply( + response -> { // cache result so step doesn't have to do it again, this is a turn cache and we // use hashcode so we don't conflict with any other qnamakerdialogs out there. dc.getState().setValue(String.format("turn.qnaresult%s", this.hashCode()), response); // disable interruption if we have answers. return !(response.getAnswers().length == 0); - }); + } + ); }); } // call base for default behavior. @@ -657,8 +693,7 @@ public class QnAMakerDialog extends WaterfallDialog { } /** - * Gets an {@link QnAMakerClient} to use to access the QnA Maker knowledge - * base. + * Gets an {@link QnAMakerClient} to use to access the QnA Maker knowledge base. * * @param dc The {@link DialogContext} for the current turn of conversation. * @return A Task representing the asynchronous operation. If the task is @@ -679,8 +714,9 @@ public class QnAMakerDialog extends WaterfallDialog { } }; - return this.getQnAMakerOptions(dc).thenApply(options -> new QnAMaker(endpoint, options, - this.getTelemetryClient(), this.logPersonalInformation)); + return this.getQnAMakerOptions( + dc + ).thenApply(options -> new QnAMaker(endpoint, options, this.getTelemetryClient(), this.logPersonalInformation)); } /** @@ -717,12 +753,10 @@ public class QnAMakerDialog extends WaterfallDialog { return CompletableFuture.completedFuture(new QnADialogResponseOptions() { { setNoAnswer(noAnswer.bind(dc, dc.getState()).join()); - setActiveLearningCardTitle(activeLearningCardTitle != null - ? activeLearningCardTitle - : DEFAULT_CARD_TITLE); - setCardNoMatchText( - cardNoMatchText != null ? cardNoMatchText - : DEFAULT_CARD_NO_MATCH_TEXT); + setActiveLearningCardTitle( + activeLearningCardTitle != null ? activeLearningCardTitle : DEFAULT_CARD_TITLE + ); + setCardNoMatchText(cardNoMatchText != null ? cardNoMatchText : DEFAULT_CARD_NO_MATCH_TEXT); setCardNoMatchResponse(cardNoMatchResponse.bind(dc, null).join()); } }); @@ -736,8 +770,8 @@ public class QnAMakerDialog extends WaterfallDialog { * @return An object of Task of type {@link DialogTurnResult}. */ protected CompletableFuture displayQnAResult(WaterfallStepContext stepContext) { - QnAMakerDialogOptions dialogOptions = ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), - OPTIONS, QnAMakerDialogOptions.class); + QnAMakerDialogOptions dialogOptions = + ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), OPTIONS, QnAMakerDialogOptions.class); String reply = stepContext.getContext().getActivity().getText(); if (reply.compareToIgnoreCase(dialogOptions.getResponseOptions().getCardNoMatchText()) == 0) { Activity activity = dialogOptions.getResponseOptions().getCardNoMatchResponse(); @@ -751,8 +785,8 @@ public class QnAMakerDialog extends WaterfallDialog { } // If previous QnAId is present, replace the dialog - Integer previousQnAId = ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), PREVIOUS_QNA_ID, - Integer.class, 0); + Integer previousQnAId = + ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), PREVIOUS_QNA_ID, Integer.class, 0); if (previousQnAId > 0) { // restart the waterfall to step 0 return this.runStep(stepContext, 0, DialogReason.BEGIN_CALLED, null); @@ -781,10 +815,10 @@ public class QnAMakerDialog extends WaterfallDialog { // -Check if previous context is present, if yes then put it with the query // -Check for id if query is present in reverse index. - Map previousContextData = ObjectPath.getPathValue(dc.getActiveDialog().getState(), - QNA_CONTEXT_DATA, Map.class); - Integer previousQnAId = ObjectPath.getPathValue(dc.getActiveDialog().getState(), PREVIOUS_QNA_ID, - Integer.class, 0); + Map previousContextData = + ObjectPath.getPathValue(dc.getActiveDialog().getState(), QNA_CONTEXT_DATA, Map.class); + Integer previousQnAId = + ObjectPath.getPathValue(dc.getActiveDialog().getState(), PREVIOUS_QNA_ID, Integer.class, 0); if (previousQnAId > 0) { dialogOptions.getQnAMakerOptions().setContext(new QnARequestContext() { @@ -804,8 +838,8 @@ public class QnAMakerDialog extends WaterfallDialog { // clear suggestedQuestions between turns. stepContext.getState().removeValue("this.suggestedQuestions"); - QnAMakerDialogOptions dialogOptions = ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), - OPTIONS, QnAMakerDialogOptions.class); + QnAMakerDialogOptions dialogOptions = + ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), OPTIONS, QnAMakerDialogOptions.class); QnAMakerDialog.resetOptions(stepContext, dialogOptions); // Storing the context info @@ -813,12 +847,12 @@ public class QnAMakerDialog extends WaterfallDialog { // Calling QnAMaker to get response. return this.getQnAMakerClient(stepContext).thenApply(qnaMakerClient -> { - QueryResults response = (QueryResults) stepContext.getState() - .get(String.format("turn.qnaresult%s", this.hashCode())); + QueryResults response = + (QueryResults) stepContext.getState().get(String.format("turn.qnaresult%s", this.hashCode())); if (response == null) { - response = qnaMakerClient.getAnswersRaw(stepContext.getContext(), dialogOptions.getQnAMakerOptions(), - null, null) - .join(); + response = qnaMakerClient + .getAnswersRaw(stepContext.getContext(), dialogOptions.getQnAMakerOptions(), null, null) + .join(); } // Resetting previous query. @@ -833,8 +867,10 @@ public class QnAMakerDialog extends WaterfallDialog { // Check if active learning is enabled. // MaximumScoreForLowScoreVariation is the score above which no need to check // for feedback. - if (response.getAnswers().length > 0 && response.getAnswers()[0] - .getScore() <= (ActiveLearningUtils.getMaximumScoreForLowScoreVariation() / PERCENTAGE_DIVISOR)) { + if ( + response.getAnswers().length > 0 && response.getAnswers()[0] + .getScore() <= (ActiveLearningUtils.getMaximumScoreForLowScoreVariation() / PERCENTAGE_DIVISOR) + ) { // Get filtered list of the response that support low score variation criteria. response.setAnswers(qnaMakerClient.getLowScoreVariation(response.getAnswers())); @@ -845,9 +881,11 @@ public class QnAMakerDialog extends WaterfallDialog { } // Get active learning suggestion card activity. - Activity message = QnACardBuilder.getSuggestionsCard(suggestedQuestions, - dialogOptions.getResponseOptions().getActiveLearningCardTitle(), - dialogOptions.getResponseOptions().getCardNoMatchText()); + Activity message = QnACardBuilder.getSuggestionsCard( + suggestedQuestions, + dialogOptions.getResponseOptions().getActiveLearningCardTitle(), + dialogOptions.getResponseOptions().getCardNoMatchText() + ); stepContext.getContext().sendActivity(message).join(); ObjectPath.setPathValue(stepContext.getActiveDialog().getState(), OPTIONS, dialogOptions); @@ -870,33 +908,37 @@ public class QnAMakerDialog extends WaterfallDialog { } private CompletableFuture callTrain(WaterfallStepContext stepContext) { - QnAMakerDialogOptions dialogOptions = ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), OPTIONS, - QnAMakerDialogOptions.class); + QnAMakerDialogOptions dialogOptions = + ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), OPTIONS, QnAMakerDialogOptions.class); List trainResponses = (List) stepContext.getValues().get(ValueProperty.QNA_DATA); String currentQuery = (String) stepContext.getValues().get(ValueProperty.CURRENT_QUERY); String reply = stepContext.getContext().getActivity().getText(); if (trainResponses.size() > 1) { - QueryResult qnaResult = trainResponses - .stream().filter(kvp -> kvp.getQuestions()[0].equals(reply)).findFirst().orElse(null); + QueryResult qnaResult = + trainResponses.stream().filter(kvp -> kvp.getQuestions()[0].equals(reply)).findFirst().orElse(null); if (qnaResult != null) { List queryResultArr = new ArrayList(); stepContext.getValues().put(ValueProperty.QNA_DATA, queryResultArr.add(qnaResult)); - FeedbackRecord record = new FeedbackRecord() {{ - setUserId(stepContext.getContext().getActivity().getId()); - setUserQuestion(currentQuery); - setQnaId(qnaResult.getId()); - }}; + FeedbackRecord record = new FeedbackRecord() { + { + setUserId(stepContext.getContext().getActivity().getId()); + setUserQuestion(currentQuery); + setQnaId(qnaResult.getId()); + } + }; FeedbackRecord[] records = {record}; - FeedbackRecords feedbackRecords = new FeedbackRecords() {{ - setRecords(records); - }}; + FeedbackRecords feedbackRecords = new FeedbackRecords() { + { + setRecords(records); + } + }; // Call Active Learning Train API return this.getQnAMakerClient(stepContext).thenCompose(qnaClient -> { try { - return qnaClient.callTrain(feedbackRecords).thenCompose(task -> - stepContext.next(new ArrayList().add(qnaResult))); + return qnaClient.callTrain(feedbackRecords) + .thenCompose(task -> stepContext.next(new ArrayList().add(qnaResult))); } catch (IOException e) { LoggerFactory.getLogger(QnAMakerDialog.class).error("callTrain"); } @@ -921,8 +963,8 @@ public class QnAMakerDialog extends WaterfallDialog { } private CompletableFuture checkForMultiTurnPrompt(WaterfallStepContext stepContext) { - QnAMakerDialogOptions dialogOptions = ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), - OPTIONS, QnAMakerDialogOptions.class); + QnAMakerDialogOptions dialogOptions = + ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), OPTIONS, QnAMakerDialogOptions.class); List response = (List) stepContext.getResult(); if (response != null && response.size() > 0) { // -Check if context is present and prompt exists @@ -936,21 +978,21 @@ public class QnAMakerDialog extends WaterfallDialog { QueryResult answer = response.get(0); if (answer.getContext() != null && answer.getContext().getPrompts().length > 0) { - Map previousContextData = ObjectPath.getPathValue( - stepContext.getActiveDialog().getState(), QNA_CONTEXT_DATA, Map.class); + Map previousContextData = + ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), QNA_CONTEXT_DATA, Map.class); for (QnAMakerPrompt prompt : answer.getContext().getPrompts()) { previousContextData.put(prompt.getDisplayText(), prompt.getQnaId()); } - ObjectPath.setPathValue(stepContext.getActiveDialog().getState(), - QNA_CONTEXT_DATA, previousContextData); + ObjectPath + .setPathValue(stepContext.getActiveDialog().getState(), QNA_CONTEXT_DATA, previousContextData); ObjectPath.setPathValue(stepContext.getActiveDialog().getState(), PREVIOUS_QNA_ID, answer.getId()); ObjectPath.setPathValue(stepContext.getActiveDialog().getState(), OPTIONS, dialogOptions); // Get multi-turn prompts card activity. - Activity message = QnACardBuilder.getQnAPromptsCard(answer, - dialogOptions.getResponseOptions().getCardNoMatchText()); + Activity message = + QnACardBuilder.getQnAPromptsCard(answer, dialogOptions.getResponseOptions().getCardNoMatchText()); stepContext.getContext().sendActivity(message).join(); return CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.WAITING)); @@ -967,6 +1009,7 @@ public class QnAMakerDialog extends WaterfallDialog { public static final String CURRENT_QUERY = "currentQuery"; public static final String QNA_DATA = "qnaData"; - private ValueProperty() { } + private ValueProperty() { + } } } diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/models/RankerTypes.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/models/RankerTypes.java index 0cd10e87..da36cc39 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/models/RankerTypes.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/models/RankerTypes.java @@ -22,5 +22,6 @@ public final class RankerTypes { */ public static final String AUTO_SUGGEST_QUESTION = "AutoSuggestQuestion"; - private RankerTypes() { } + private RankerTypes() { + } } diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/ActiveLearningUtils.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/ActiveLearningUtils.java index 726f4cc6..3275751f 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/ActiveLearningUtils.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/ActiveLearningUtils.java @@ -32,7 +32,8 @@ public final class ActiveLearningUtils { private static Float minimumScoreForLowScoreVariation = MINIMUM_SCORE_VARIATION; - private ActiveLearningUtils() { } + private ActiveLearningUtils() { + } /** * Gets maximum Score For Low Score Variation. @@ -47,7 +48,7 @@ public final class ActiveLearningUtils { * Sets maximum Score For Low Score Variation. * * @param withMaximumScoreForLowScoreVariation Maximum Score For Low Score - * Variation. + * Variation. */ public static void setMaximumScoreForLowScoreVariation(Float withMaximumScoreForLowScoreVariation) { ActiveLearningUtils.maximumScoreForLowScoreVariation = withMaximumScoreForLowScoreVariation; @@ -66,7 +67,7 @@ public final class ActiveLearningUtils { * Sets minimum Score For Low Score Variation. * * @param withMinimumScoreForLowScoreVariation Minimum Score For Low Score - * Variation. + * Variation. */ public static void setMinimumScoreForLowScoreVariation(Float withMinimumScoreForLowScoreVariation) { ActiveLearningUtils.minimumScoreForLowScoreVariation = withMinimumScoreForLowScoreVariation; @@ -101,12 +102,17 @@ public final class ActiveLearningUtils { filteredQnaSearchResult.add(qnaSearchResults.get(0)); for (int i = 1; i < qnaSearchResults.size(); i++) { - if (ActiveLearningUtils - .includeForClustering(prevScore, qnaSearchResults.get(i).getScore() * PERCENTAGE_DIVISOR, - ActiveLearningUtils.PREVIOUS_LOW_SCORE_VARIATION_MULTIPLIER) - && ActiveLearningUtils.includeForClustering(topAnswerScore, - qnaSearchResults.get(i).getScore() * PERCENTAGE_DIVISOR, - ActiveLearningUtils.MAX_LOW_SCORE_VARIATION_MULTIPLIER)) { + if ( + ActiveLearningUtils.includeForClustering( + prevScore, + qnaSearchResults.get(i).getScore() * PERCENTAGE_DIVISOR, + ActiveLearningUtils.PREVIOUS_LOW_SCORE_VARIATION_MULTIPLIER + ) && ActiveLearningUtils.includeForClustering( + topAnswerScore, + qnaSearchResults.get(i).getScore() * PERCENTAGE_DIVISOR, + ActiveLearningUtils.MAX_LOW_SCORE_VARIATION_MULTIPLIER + ) + ) { prevScore = qnaSearchResults.get(i).getScore() * PERCENTAGE_DIVISOR; filteredQnaSearchResult.add(qnaSearchResults.get(i)); } diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/BindToActivity.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/BindToActivity.java index 9126f006..0262ba9b 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/BindToActivity.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/BindToActivity.java @@ -18,6 +18,7 @@ public class BindToActivity { /** * Construct to bind an Activity. + * * @param withActivity activity to bind. */ public BindToActivity(Activity withActivity) { @@ -27,7 +28,7 @@ public class BindToActivity { /** * * @param context The context. - * @param data The data. + * @param data The data. * @return The activity. */ public CompletableFuture bind(DialogContext context, @Nullable Object data) { @@ -36,6 +37,7 @@ public class BindToActivity { /** * Get the activity text. + * * @return The activity text. */ public String toString() { diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/GenerateAnswerUtils.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/GenerateAnswerUtils.java index 05178903..9436b7a7 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/GenerateAnswerUtils.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/GenerateAnswerUtils.java @@ -3,6 +3,7 @@ package com.microsoft.bot.ai.qna.utils; +import com.microsoft.bot.connector.Async; import java.io.IOException; import java.util.Arrays; import java.util.List; @@ -39,11 +40,10 @@ public class GenerateAnswerUtils { /** * Initializes a new instance of the {@link GenerateAnswerUtils} class. * - * @param withEndpoint QnA Maker endpoint details. - * @param withOptions QnA Maker options. + * @param withEndpoint QnA Maker endpoint details. + * @param withOptions QnA Maker options. */ - public GenerateAnswerUtils(QnAMakerEndpoint withEndpoint, - QnAMakerOptions withOptions) { + public GenerateAnswerUtils(QnAMakerEndpoint withEndpoint, QnAMakerOptions withOptions) { this.endpoint = withEndpoint; this.options = withOptions != null ? withOptions : new QnAMakerOptions(); @@ -74,15 +74,18 @@ public class GenerateAnswerUtils { * @param turnContext The Turn Context that contains the user question to be * queried against your knowledge base. * @param messageActivity Message activity of the turn context. - * @param withOptions The options for the QnA Maker knowledge base. If null, + * @param withOptions The options for the QnA Maker knowledge base. If null, * constructor option is used for this instance. * @return A list of answers for the user query, sorted in decreasing order of * ranking score. * @throws IOException IOException */ @Deprecated - public CompletableFuture getAnswers(TurnContext turnContext, Activity messageActivity, - QnAMakerOptions withOptions) throws IOException { + public CompletableFuture getAnswers( + TurnContext turnContext, + Activity messageActivity, + QnAMakerOptions withOptions + ) throws IOException { return this.getAnswersRaw(turnContext, messageActivity, withOptions).thenApply(result -> result.getAnswers()); } @@ -92,24 +95,34 @@ public class GenerateAnswerUtils { * @param turnContext The Turn Context that contains the user question to be * queried against your knowledge base. * @param messageActivity Message activity of the turn context. - * @param withOptions The options for the QnA Maker knowledge base. If null, + * @param withOptions The options for the QnA Maker knowledge base. If null, * constructor option is used for this instance. * @return A list of answers for the user query, sorted in decreasing order of * ranking score. */ - public CompletableFuture getAnswersRaw(TurnContext turnContext, Activity messageActivity, - QnAMakerOptions withOptions) { + public CompletableFuture getAnswersRaw( + TurnContext turnContext, + Activity messageActivity, + QnAMakerOptions withOptions + ) { if (turnContext == null) { - throw new IllegalArgumentException("turnContext"); + return Async.completeExceptionally(new IllegalArgumentException("turnContext")); } if (turnContext.getActivity() == null) { - throw new IllegalArgumentException(String.format("The %1$s property for %2$s can't be null: turnContext", - turnContext.getActivity(), "turnContext")); + return Async.completeExceptionally( + new IllegalArgumentException( + String.format( + "The %1$s property for %2$s can't be null: turnContext", + turnContext.getActivity(), + "turnContext" + ) + ) + ); } if (messageActivity == null) { - throw new IllegalArgumentException("Activity type is not a message"); + return Async.completeExceptionally(new IllegalArgumentException("Activity type is not a message")); } QnAMakerOptions hydratedOptions = this.hydrateOptions(withOptions); @@ -126,8 +139,10 @@ public class GenerateAnswerUtils { } } - private static CompletableFuture formatQnAResult(JsonNode response, QnAMakerOptions options) - throws IOException { + private static CompletableFuture formatQnAResult( + JsonNode response, + QnAMakerOptions options + ) throws IOException { String jsonResponse = null; JacksonAdapter jacksonAdapter = new JacksonAdapter(); QueryResults results = null; @@ -137,8 +152,10 @@ public class GenerateAnswerUtils { for (QueryResult answer : results.getAnswers()) { answer.setScore(answer.getScore() / PERCENTAGE_DIVISOR); } - List answerList = Arrays.asList(results.getAnswers()). - stream().filter(answer -> answer.getScore() > options.getScoreThreshold()).collect(Collectors.toList()); + List answerList = Arrays.asList(results.getAnswers()) + .stream() + .filter(answer -> answer.getScore() > options.getScoreThreshold()) + .collect(Collectors.toList()); results.setAnswers(answerList.toArray(new QueryResult[answerList.size()])); return CompletableFuture.completedFuture(results); @@ -154,8 +171,9 @@ public class GenerateAnswerUtils { } if (options.getScoreThreshold() < 0 || options.getScoreThreshold() > 1) { - throw new IllegalArgumentException(String - .format("options: The %s property should be a value between 0 and 1", options.getScoreThreshold())); + throw new IllegalArgumentException( + String.format("options: The %s property should be a value between 0 and 1", options.getScoreThreshold()) + ); } if (options.getTimeout() == 0.0d) { @@ -187,15 +205,16 @@ public class GenerateAnswerUtils { QnAMakerOptions hydratedOptions = null; try { - hydratedOptions = jacksonAdapter.deserialize(jacksonAdapter.serialize(options), - QnAMakerOptions.class); + hydratedOptions = jacksonAdapter.deserialize(jacksonAdapter.serialize(options), QnAMakerOptions.class); } catch (IOException e) { LoggerFactory.getLogger(GenerateAnswerUtils.class).error("hydrateOptions"); } if (queryOptions != null) { - if (queryOptions.getScoreThreshold() != hydratedOptions.getScoreThreshold() - && queryOptions.getScoreThreshold() != 0) { + if ( + queryOptions.getScoreThreshold() != hydratedOptions.getScoreThreshold() + && queryOptions.getScoreThreshold() != 0 + ) { hydratedOptions.setScoreThreshold(queryOptions.getScoreThreshold()); } @@ -210,18 +229,24 @@ public class GenerateAnswerUtils { hydratedOptions.setContext(queryOptions.getContext()); hydratedOptions.setQnAId(queryOptions.getQnAId()); hydratedOptions.setIsTest(queryOptions.getIsTest()); - hydratedOptions.setRankerType(queryOptions.getRankerType() != null ? queryOptions.getRankerType() - : RankerTypes.DEFAULT_RANKER_TYPE); + hydratedOptions.setRankerType( + queryOptions.getRankerType() != null ? queryOptions.getRankerType() : RankerTypes.DEFAULT_RANKER_TYPE + ); hydratedOptions.setStrictFiltersJoinOperator(queryOptions.getStrictFiltersJoinOperator()); } return hydratedOptions; } - private CompletableFuture queryQnaService(Activity messageActivity, QnAMakerOptions withOptions) - throws IOException { - String requestUrl = String.format("%1$s/knowledgebases/%2$s/generateanswer", this.endpoint.getHost(), - this.endpoint.getKnowledgeBaseId()); + private CompletableFuture queryQnaService( + Activity messageActivity, + QnAMakerOptions withOptions + ) throws IOException { + String requestUrl = String.format( + "%1$s/knowledgebases/%2$s/generateanswer", + this.endpoint.getHost(), + this.endpoint.getKnowledgeBaseId() + ); JacksonAdapter jacksonAdapter = new JacksonAdapter(); String jsonRequest = null; @@ -250,8 +275,12 @@ public class GenerateAnswerUtils { }); } - private CompletableFuture emitTraceInfo(TurnContext turnContext, Activity messageActivity, - QueryResult[] result, QnAMakerOptions withOptions) { + private CompletableFuture emitTraceInfo( + TurnContext turnContext, + Activity messageActivity, + QueryResult[] result, + QnAMakerOptions withOptions + ) { String knowledgeBaseId = this.endpoint.getKnowledgeBaseId(); QnAMakerTraceInfo traceInfo = new QnAMakerTraceInfo() { { @@ -267,8 +296,12 @@ public class GenerateAnswerUtils { setRankerType(withOptions.getRankerType()); } }; - Activity traceActivity = Activity.createTraceActivity(QnAMaker.QNA_MAKER_NAME, QnAMaker.QNA_MAKER_TRACE_TYPE, - traceInfo, QnAMaker.QNA_MAKER_TRACE_LABEL); + Activity traceActivity = Activity.createTraceActivity( + QnAMaker.QNA_MAKER_NAME, + QnAMaker.QNA_MAKER_TRACE_TYPE, + traceInfo, + QnAMaker.QNA_MAKER_TRACE_LABEL + ); return turnContext.sendActivity(traceActivity).thenApply(response -> null); } } diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/HttpRequestUtils.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/HttpRequestUtils.java index dcfff3e5..5e60838c 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/HttpRequestUtils.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/HttpRequestUtils.java @@ -3,8 +3,8 @@ package com.microsoft.bot.ai.qna.utils; +import com.microsoft.bot.connector.Async; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -25,6 +25,7 @@ import org.slf4j.LoggerFactory; */ public class HttpRequestUtils { private OkHttpClient httpClient = new OkHttpClient(); + /** * Execute Http request. * @@ -33,18 +34,23 @@ public class HttpRequestUtils { * @param endpoint QnA Maker endpoint details. * @return Returns http response object. */ - public CompletableFuture executeHttpRequest(String requestUrl, String payloadBody, - QnAMakerEndpoint endpoint) { + public CompletableFuture executeHttpRequest( + String requestUrl, + String payloadBody, + QnAMakerEndpoint endpoint + ) { if (requestUrl == null) { - throw new IllegalArgumentException("requestUrl: Request url can not be null."); + return Async + .completeExceptionally(new IllegalArgumentException("requestUrl: Request url can not be null.")); } if (payloadBody == null) { - throw new IllegalArgumentException("payloadBody: Payload body can not be null."); + return Async + .completeExceptionally(new IllegalArgumentException("payloadBody: Payload body can not be null.")); } if (endpoint == null) { - throw new IllegalArgumentException("endpoint"); + return Async.completeExceptionally(new IllegalArgumentException("endpoint")); } ObjectMapper mapper = new ObjectMapper(); @@ -57,11 +63,11 @@ public class HttpRequestUtils { qnaResponse = mapper.readTree(response.body().string()); if (!response.isSuccessful()) { String message = "Unexpected code " + response.code(); - throw new Exception(message); + return Async.completeExceptionally(new Exception(message)); } } catch (Exception e) { LoggerFactory.getLogger(HttpRequestUtils.class).error("findPackages", e); - throw new CompletionException(e); + return Async.completeExceptionally(e); } return CompletableFuture.completedFuture(qnaResponse); @@ -69,15 +75,15 @@ public class HttpRequestUtils { private Request buildRequest(String requestUrl, String endpointKey, RequestBody body) { HttpUrl.Builder httpBuilder = HttpUrl.parse(requestUrl).newBuilder(); - Request.Builder requestBuilder = new Request.Builder() - .url(httpBuilder.build()) + Request.Builder requestBuilder = new Request.Builder().url(httpBuilder.build()) .addHeader("Authorization", String.format("EndpointKey %s", endpointKey)) - .addHeader("Ocp-Apim-Subscription-Key", endpointKey).addHeader("User-Agent", UserAgent.value()) + .addHeader("Ocp-Apim-Subscription-Key", endpointKey) + .addHeader("User-Agent", UserAgent.value()) .post(body); return requestBuilder.build(); } private RequestBody buildRequestBody(String payloadBody) throws JsonProcessingException { - return RequestBody.create(payloadBody, MediaType.parse("application/json; charset=utf-8")); + return RequestBody.create(MediaType.parse("application/json; charset=utf-8"), payloadBody); } } diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/QnACardBuilder.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/QnACardBuilder.java index c2b7bb11..5f339ca0 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/QnACardBuilder.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/QnACardBuilder.java @@ -19,7 +19,8 @@ import com.microsoft.bot.schema.HeroCard; */ public final class QnACardBuilder { - private QnACardBuilder() { } + private QnACardBuilder() { + } /** * Get active learning suggestions card. diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/QnATelemetryConstants.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/QnATelemetryConstants.java index 0fad3586..9827f6bd 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/QnATelemetryConstants.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/QnATelemetryConstants.java @@ -8,7 +8,8 @@ package com.microsoft.bot.ai.qna.utils; */ public final class QnATelemetryConstants { - private QnATelemetryConstants() { } + private QnATelemetryConstants() { + } /** * The Key used for the custom event type within telemetry. diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/TrainUtils.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/TrainUtils.java index d7fa020c..3bdb9a08 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/TrainUtils.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/TrainUtils.java @@ -5,6 +5,7 @@ package com.microsoft.bot.ai.qna.utils; import com.microsoft.bot.ai.qna.QnAMakerEndpoint; import com.microsoft.bot.ai.qna.models.FeedbackRecords; +import com.microsoft.bot.connector.Async; import com.microsoft.bot.restclient.serializer.JacksonAdapter; import java.io.IOException; @@ -34,7 +35,9 @@ public class TrainUtils { */ public CompletableFuture callTrain(FeedbackRecords feedbackRecords) throws IOException { if (feedbackRecords == null) { - throw new IllegalArgumentException("feedbackRecords: Feedback records cannot be null."); + return Async.completeExceptionally( + new IllegalArgumentException("feedbackRecords: Feedback records cannot be null.") + ); } if (feedbackRecords.getRecords() == null || feedbackRecords.getRecords().length == 0) { @@ -46,8 +49,8 @@ public class TrainUtils { } private CompletableFuture queryTrain(FeedbackRecords feedbackRecords) throws IOException { - String requestUrl = String.format("%1$s/knowledgebases/%2$s/train", this.endpoint.getHost(), - this.endpoint.getKnowledgeBaseId()); + String requestUrl = String + .format("%1$s/knowledgebases/%2$s/train", this.endpoint.getHost(), this.endpoint.getKnowledgeBaseId()); JacksonAdapter jacksonAdapter = new JacksonAdapter(); String jsonRequest = jacksonAdapter.serialize(feedbackRecords); diff --git a/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerRecognizerTests.java b/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerRecognizerTests.java index cdb2f31d..7443b01f 100644 --- a/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerRecognizerTests.java +++ b/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerRecognizerTests.java @@ -98,6 +98,7 @@ public class QnAMakerRecognizerTests { Assert.assertEquals(result.getIntents().get("QnAMatch"), null); Assert.assertNotEquals(result.getIntents().get("None"), null); } catch (Exception e) { + e.printStackTrace(); fail(); } finally { try { diff --git a/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerTests.java b/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerTests.java index 160b8ce7..25390899 100644 --- a/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerTests.java +++ b/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerTests.java @@ -197,7 +197,7 @@ public class QnAMakerTests { } }; TurnContext context = new TurnContextImpl(adapter, activity); - Assert.assertThrows(IllegalArgumentException.class, () -> qna.getAnswers(context, null)); + Assert.assertThrows(CompletionException.class, () -> qna.getAnswers(context, null).join()); } catch (Exception e) { fail(); } finally { @@ -229,7 +229,7 @@ public class QnAMakerTests { } }; TurnContext context = new TurnContextImpl(adapter, activity); - Assert.assertThrows(IllegalArgumentException.class, () -> qna.getAnswers(context, null)); + Assert.assertThrows(CompletionException.class, () -> qna.getAnswers(context, null).join()); } catch (Exception e) { fail(); } finally { @@ -248,7 +248,7 @@ public class QnAMakerTests { // Get basic Qna QnAMaker qna = this.qnaReturnsAnswer(mockWebServer); - Assert.assertThrows(IllegalArgumentException.class, () -> qna.getAnswers(null, null)); + Assert.assertThrows(CompletionException.class, () -> qna.getAnswers(null, null).join()); } catch (Exception e) { fail(); } finally { @@ -281,7 +281,7 @@ public class QnAMakerTests { }; TurnContext context = new TurnContextImpl(adapter, activity); - Assert.assertThrows(IllegalArgumentException.class, () -> qna.getAnswers(context, null)); + Assert.assertThrows(CompletionException.class, () -> qna.getAnswers(context, null).join()); } catch (Exception e) { fail(); } finally { @@ -304,7 +304,7 @@ public class QnAMakerTests { TestAdapter adapter = new TestAdapter( TestAdapter.createConversationReference("QnaMaker_TraceActivity_NullActivity", "User1", "Bot")); TurnContext context = new MyTurnContext(adapter, null); - Assert.assertThrows(IllegalArgumentException.class, () -> qna.getAnswers(context, null)); + Assert.assertThrows(CompletionException.class, () -> qna.getAnswers(context, null).join()); } catch (Exception e) { fail(); } finally { @@ -1076,7 +1076,7 @@ public class QnAMakerTests { } }; QnAMaker qna = new QnAMaker(qnAMakerEndpoint, null); - Assert.assertThrows(CompletionException.class, () -> qna.getAnswers(getContext("how do I clean the stove?"), null)); + Assert.assertThrows(CompletionException.class, () -> qna.getAnswers(getContext("how do I clean the stove?"), null).join()); } catch (Exception e) { fail(); } finally { diff --git a/libraries/bot-connector/pom.xml b/libraries/bot-connector/pom.xml index 0e04806b..f892898c 100644 --- a/libraries/bot-connector/pom.xml +++ b/libraries/bot-connector/pom.xml @@ -60,31 +60,31 @@ guava 30.1-jre + com.squareup.retrofit2 retrofit 2.5.0 - - com.squareup.okhttp3 - okhttp - 3.12.2 - - - com.squareup.okhttp3 - logging-interceptor - 3.12.2 - - - com.squareup.okhttp3 - okhttp-urlconnection - 3.12.2 - com.squareup.retrofit2 converter-jackson 2.5.0 + + + com.squareup.okhttp3 + okhttp + + + com.squareup.okhttp3 + logging-interceptor + + + com.squareup.okhttp3 + okhttp-urlconnection + + com.microsoft.azure azure-annotations diff --git a/pom.xml b/pom.xml index c2a9d09d..40936bc9 100644 --- a/pom.xml +++ b/pom.xml @@ -311,6 +311,28 @@ test + + com.squareup.okhttp3 + okhttp + 3.12.2 + + + com.squareup.okhttp3 + logging-interceptor + 3.12.2 + + + com.squareup.okhttp3 + okhttp-urlconnection + 3.12.2 + + + com.squareup.okhttp3 + mockwebserver + 3.12.2 + test + + com.microsoft.bot bot-schema