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.
This commit is contained in:
tracyboehrer 2021-02-23 13:45:41 -06:00 коммит произвёл GitHub
Родитель 7b2d3a47c1
Коммит de1867d1a2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 460 добавлений и 279 удалений

Просмотреть файл

@ -49,14 +49,20 @@
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.bot</groupId>
<artifactId>bot-builder</artifactId>
<version>${project.version}</version>
<artifactId>bot-integration-spring</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.microsoft.bot</groupId>
@ -69,27 +75,16 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.microsoft.bot</groupId>
<artifactId>bot-integration-spring</artifactId>
<version>4.6.0-preview8</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>4.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
</dependencies>
<profiles>

Просмотреть файл

@ -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<QueryResult[]> getAnswers(TurnContext turnContext, QnAMakerOptions options,
Map<String, String> telemetryProperties, @Nullable Map<String, Double> telemetryMetrics) {
public CompletableFuture<QueryResult[]> getAnswers(
TurnContext turnContext,
QnAMakerOptions options,
Map<String, String> telemetryProperties,
@Nullable Map<String, Double> 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<QueryResults> getAnswersRaw(TurnContext turnContext, QnAMakerOptions options,
@Nullable Map<String, String> telemetryProperties, @Nullable Map<String, Double> telemetryMetrics) {
public CompletableFuture<QueryResults> getAnswersRaw(
TurnContext turnContext,
QnAMakerOptions options,
@Nullable Map<String, String> telemetryProperties,
@Nullable Map<String, Double> 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<Void> onQnaResults(QueryResult[] queryResults, TurnContext turnContext,
@Nullable Map<String, String> telemetryProperties, @Nullable Map<String, Double> 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<Void> onQnaResults(
QueryResult[] queryResults,
TurnContext turnContext,
@Nullable Map<String, String> telemetryProperties,
@Nullable Map<String, Double> 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<Pair<Map<String, String>, Map<String, Double>>> fillQnAEvent(QueryResult[] queryResults,
TurnContext turnContext, @Nullable Map<String, String> telemetryProperties,
@Nullable Map<String, Double> telemetryMetrics) throws IOException {
protected CompletableFuture<Pair<Map<String, String>, Map<String, Double>>> fillQnAEvent(
QueryResult[] queryResults,
TurnContext turnContext,
@Nullable Map<String, String> telemetryProperties,
@Nullable Map<String, Double> telemetryMetrics
) throws IOException {
Map<String, String> properties = new HashMap<String, String>();
Map<String, Double> metrics = new HashMap<String, Double>();
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<String, String> multiMapTelemetryProperties = LinkedListMultimap.create();
for (Entry<String, String> entry: telemetryProperties.entrySet()) {
for (Entry<String, String> entry : telemetryProperties.entrySet()) {
multiMapTelemetryProperties.put(entry.getKey(), entry.getValue());
}
for (Entry<String, String> entry: properties.entrySet()) {
for (Entry<String, String> entry : properties.entrySet()) {
multiMapTelemetryProperties.put(entry.getKey(), entry.getValue());
}
for (Entry<String, Collection<String>> entry: multiMapTelemetryProperties.asMap().entrySet()) {
for (Entry<String, Collection<String>> 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<String, Double> multiMapTelemetryMetrics = LinkedListMultimap.create();
for (Entry<String, Double> entry: telemetryMetrics.entrySet()) {
for (Entry<String, Double> entry : telemetryMetrics.entrySet()) {
multiMapTelemetryMetrics.put(entry.getKey(), entry.getValue());
}
for (Entry<String, Double> entry: metrics.entrySet()) {
for (Entry<String, Double> entry : metrics.entrySet()) {
multiMapTelemetryMetrics.put(entry.getKey(), entry.getValue());
}
for (Entry<String, Collection<Double>> entry: multiMapTelemetryMetrics.asMap().entrySet()) {
for (Entry<String, Collection<Double>> entry : multiMapTelemetryMetrics.asMap().entrySet()) {
telemetryMetrics.put(entry.getKey(), entry.getValue().iterator().next());
}
}

Просмотреть файл

@ -33,8 +33,12 @@ public interface QnAMakerClient {
* @return A list of answers for the user query, sorted in decreasing order of
* ranking score.
*/
CompletableFuture<QueryResult[]> getAnswers(TurnContext turnContext, QnAMakerOptions options,
Map<String, String> telemetryProperties, @Nullable Map<String, Double> telemetryMetrics);
CompletableFuture<QueryResult[]> getAnswers(
TurnContext turnContext,
QnAMakerOptions options,
Map<String, String> telemetryProperties,
@Nullable Map<String, Double> 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<QueryResults> getAnswersRaw(TurnContext turnContext, QnAMakerOptions options,
@Nullable Map<String, String> telemetryProperties, @Nullable Map<String, Double> telemetryMetrics);
CompletableFuture<QueryResults> getAnswersRaw(
TurnContext turnContext,
QnAMakerOptions options,
@Nullable Map<String, String> telemetryProperties,
@Nullable Map<String, Double> telemetryMetrics
);
/**
* Filters the ambiguous question for active learning.

Просмотреть файл

@ -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<RecognizerResult> recognize(DialogContext dialogContext, Activity activity,
Map<String, String> telemetryProperties, Map<String, Double> telemetryMetrics) {
public CompletableFuture<RecognizerResult> recognize(
DialogContext dialogContext,
Activity activity,
Map<String, String> telemetryProperties,
Map<String, Double> telemetryMetrics
) {
// Identify matched intents
RecognizerResult recognizerResult = new RecognizerResult();
recognizerResult.setText(activity.getText());
@ -364,14 +368,11 @@ public class QnAMakerRecognizer extends Recognizer {
List<Metadata> filters = new ArrayList<Metadata>();
// 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<String, String> fillRecognizerResultTelemetryProperties(RecognizerResult recognizerResult,
Map<String, String> telemetryProperties, @Nullable DialogContext dialogContext) {
protected Map<String, String> fillRecognizerResultTelemetryProperties(
RecognizerResult recognizerResult,
Map<String, String> 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<String, String> properties = new HashMap<String, String>() {
{
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<String, List<String>> 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<String, List<String>> 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;

Просмотреть файл

@ -50,6 +50,10 @@ public interface TelemetryQnAMaker {
* @return A list of answers for the user query, sorted in decreasing order of
* ranking score.
*/
CompletableFuture<QueryResult[]> getAnswers(TurnContext turnContext, QnAMakerOptions options,
Map<String, String> telemetryProperties, @Nullable Map<String, Double> telemetryMetrics);
CompletableFuture<QueryResult[]> getAnswers(
TurnContext turnContext,
QnAMakerOptions options,
Map<String, String> telemetryProperties,
@Nullable Map<String, Double> telemetryMetrics
);
}

Просмотреть файл

@ -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<DialogTurnResult> 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<String> suggestedQuestions = (List<String>) 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<DialogTurnResult> 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<String, Integer> 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<String, Integer> 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<DialogTurnResult> callTrain(WaterfallStepContext stepContext) {
QnAMakerDialogOptions dialogOptions = ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), OPTIONS,
QnAMakerDialogOptions.class);
QnAMakerDialogOptions dialogOptions =
ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), OPTIONS, QnAMakerDialogOptions.class);
List<QueryResult> trainResponses = (List<QueryResult>) 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<QueryResult> queryResultArr = new ArrayList<QueryResult>();
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<QueryResult>().add(qnaResult)));
return qnaClient.callTrain(feedbackRecords)
.thenCompose(task -> stepContext.next(new ArrayList<QueryResult>().add(qnaResult)));
} catch (IOException e) {
LoggerFactory.getLogger(QnAMakerDialog.class).error("callTrain");
}
@ -921,8 +963,8 @@ public class QnAMakerDialog extends WaterfallDialog {
}
private CompletableFuture<DialogTurnResult> checkForMultiTurnPrompt(WaterfallStepContext stepContext) {
QnAMakerDialogOptions dialogOptions = ObjectPath.getPathValue(stepContext.getActiveDialog().getState(),
OPTIONS, QnAMakerDialogOptions.class);
QnAMakerDialogOptions dialogOptions =
ObjectPath.getPathValue(stepContext.getActiveDialog().getState(), OPTIONS, QnAMakerDialogOptions.class);
List<QueryResult> response = (List<QueryResult>) 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<String, Integer> previousContextData = ObjectPath.getPathValue(
stepContext.getActiveDialog().getState(), QNA_CONTEXT_DATA, Map.class);
Map<String, Integer> 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() {
}
}
}

Просмотреть файл

@ -22,5 +22,6 @@ public final class RankerTypes {
*/
public static final String AUTO_SUGGEST_QUESTION = "AutoSuggestQuestion";
private RankerTypes() { }
private RankerTypes() {
}
}

Просмотреть файл

@ -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));
}

Просмотреть файл

@ -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<Activity> bind(DialogContext context, @Nullable Object data) {
@ -36,6 +37,7 @@ public class BindToActivity {
/**
* Get the activity text.
*
* @return The activity text.
*/
public String toString() {

Просмотреть файл

@ -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<QueryResult[]> getAnswers(TurnContext turnContext, Activity messageActivity,
QnAMakerOptions withOptions) throws IOException {
public CompletableFuture<QueryResult[]> 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<QueryResults> getAnswersRaw(TurnContext turnContext, Activity messageActivity,
QnAMakerOptions withOptions) {
public CompletableFuture<QueryResults> 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<QueryResults> formatQnAResult(JsonNode response, QnAMakerOptions options)
throws IOException {
private static CompletableFuture<QueryResults> 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<QueryResult> answerList = Arrays.asList(results.getAnswers()).
stream().filter(answer -> answer.getScore() > options.getScoreThreshold()).collect(Collectors.toList());
List<QueryResult> 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<QueryResults> 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<QueryResults> 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<Void> emitTraceInfo(TurnContext turnContext, Activity messageActivity,
QueryResult[] result, QnAMakerOptions withOptions) {
private CompletableFuture<Void> 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);
}
}

Просмотреть файл

@ -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<JsonNode> executeHttpRequest(String requestUrl, String payloadBody,
QnAMakerEndpoint endpoint) {
public CompletableFuture<JsonNode> 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);
}
}

Просмотреть файл

@ -19,7 +19,8 @@ import com.microsoft.bot.schema.HeroCard;
*/
public final class QnACardBuilder {
private QnACardBuilder() { }
private QnACardBuilder() {
}
/**
* Get active learning suggestions card.

Просмотреть файл

@ -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.

Просмотреть файл

@ -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<Void> 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<Void> 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);

Просмотреть файл

@ -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 {

Просмотреть файл

@ -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 {

Просмотреть файл

@ -60,31 +60,31 @@
<artifactId>guava</artifactId>
<version>30.1-jre</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.12.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>3.12.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-urlconnection</artifactId>
<version>3.12.2</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-jackson</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-urlconnection</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-annotations</artifactId>

22
pom.xml
Просмотреть файл

@ -311,6 +311,28 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.12.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>3.12.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-urlconnection</artifactId>
<version>3.12.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>3.12.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.microsoft.bot</groupId>
<artifactId>bot-schema</artifactId>