Skip to content

Commit a275712

Browse files
authored
Merge branch 'master' into axsuarez/integration-and-skills-tests
2 parents 406f4c9 + a1a4ed6 commit a275712

File tree

56 files changed

+1094
-113
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1094
-113
lines changed

libraries/botbuilder-ai/botbuilder/ai/qna/models/generate_answer_request_body.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .metadata import Metadata
99
from .qna_request_context import QnARequestContext
10+
from .ranker_types import RankerTypes
1011

1112

1213
class GenerateAnswerRequestBody(Model):
@@ -19,6 +20,8 @@ class GenerateAnswerRequestBody(Model):
1920
"strict_filters": {"key": "strictFilters", "type": "[Metadata]"},
2021
"context": {"key": "context", "type": "QnARequestContext"},
2122
"qna_id": {"key": "qnaId", "type": "int"},
23+
"is_test": {"key": "isTest", "type": "bool"},
24+
"ranker_type": {"key": "rankerType", "type": "RankerTypes"},
2225
}
2326

2427
def __init__(
@@ -29,6 +32,8 @@ def __init__(
2932
strict_filters: List[Metadata],
3033
context: QnARequestContext = None,
3134
qna_id: int = None,
35+
is_test: bool = False,
36+
ranker_type: str = RankerTypes.DEFAULT,
3237
**kwargs
3338
):
3439
"""
@@ -47,6 +52,10 @@ def __init__(
4752
4853
qna_id: Id of the current question asked.
4954
55+
is_test: (Optional) A value indicating whether to call test or prod environment of knowledgebase.
56+
57+
ranker_types: (Optional) Ranker types.
58+
5059
"""
5160

5261
super().__init__(**kwargs)
@@ -57,3 +66,5 @@ def __init__(
5766
self.strict_filters = strict_filters
5867
self.context = context
5968
self.qna_id = qna_id
69+
self.is_test = is_test
70+
self.ranker_type = ranker_type

libraries/botbuilder-ai/botbuilder/ai/qna/models/qnamaker_trace_info.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .metadata import Metadata
88
from .query_result import QueryResult
99
from .qna_request_context import QnARequestContext
10+
from .ranker_types import RankerTypes
1011

1112

1213
class QnAMakerTraceInfo:
@@ -22,6 +23,8 @@ def __init__(
2223
strict_filters: List[Metadata],
2324
context: QnARequestContext = None,
2425
qna_id: int = None,
26+
is_test: bool = False,
27+
ranker_type: str = RankerTypes.DEFAULT,
2528
):
2629
"""
2730
Parameters:
@@ -42,6 +45,10 @@ def __init__(
4245
context: (Optional) The context from which the QnA was extracted.
4346
4447
qna_id: (Optional) Id of the current question asked.
48+
49+
is_test: (Optional) A value indicating whether to call test or prod environment of knowledgebase.
50+
51+
ranker_types: (Optional) Ranker types.
4552
"""
4653
self.message = message
4754
self.query_results = query_results
@@ -51,3 +58,5 @@ def __init__(
5158
self.strict_filters = strict_filters
5259
self.context = context
5360
self.qna_id = qna_id
61+
self.is_test = is_test
62+
self.ranker_type = ranker_type
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
5+
class RankerTypes:
6+
7+
""" Default Ranker Behaviour. i.e. Ranking based on Questions and Answer. """
8+
9+
DEFAULT = "Default"
10+
11+
""" Ranker based on question Only. """
12+
QUESTION_ONLY = "QuestionOnly"
13+
14+
""" Ranker based on Autosuggest for question field only. """
15+
AUTO_SUGGEST_QUESTION = "AutoSuggestQuestion"

libraries/botbuilder-ai/botbuilder/ai/qna/qnamaker_options.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Licensed under the MIT License.
33

44
from .models import Metadata, QnARequestContext
5+
from .models.ranker_types import RankerTypes
56

67
# figure out if 300 milliseconds is ok for python requests library...or 100000
78
class QnAMakerOptions:
@@ -13,10 +14,14 @@ def __init__(
1314
strict_filters: [Metadata] = None,
1415
context: [QnARequestContext] = None,
1516
qna_id: int = None,
17+
is_test: bool = False,
18+
ranker_type: bool = RankerTypes.DEFAULT,
1619
):
1720
self.score_threshold = score_threshold
1821
self.timeout = timeout
1922
self.top = top
2023
self.strict_filters = strict_filters or []
2124
self.context = context
2225
self.qna_id = qna_id
26+
self.is_test = is_test
27+
self.ranker_type = ranker_type

libraries/botbuilder-ai/botbuilder/ai/qna/utils/generate_answer_utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ def _hydrate_options(self, query_options: QnAMakerOptions) -> QnAMakerOptions:
139139

140140
hydrated_options.context = query_options.context
141141
hydrated_options.qna_id = query_options.qna_id
142+
hydrated_options.is_test = query_options.is_test
143+
hydrated_options.ranker_type = query_options.ranker_type
142144

143145
return hydrated_options
144146

@@ -154,6 +156,8 @@ async def _query_qna_service(
154156
strict_filters=options.strict_filters,
155157
context=options.context,
156158
qna_id=options.qna_id,
159+
is_test=options.is_test,
160+
ranker_type=options.ranker_type,
157161
)
158162

159163
http_request_helper = HttpRequestUtils(self._http_client)
@@ -178,6 +182,8 @@ async def _emit_trace_info(
178182
strict_filters=options.strict_filters,
179183
context=options.context,
180184
qna_id=options.qna_id,
185+
is_test=options.is_test,
186+
ranker_type=options.ranker_type,
181187
)
182188

183189
trace_activity = Activity(
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"activeLearningEnabled": true,
3+
"answers": [
4+
{
5+
"questions": [],
6+
"answer": "No good match found in KB.",
7+
"score": 0,
8+
"id": -1,
9+
"source": null,
10+
"metadata": []
11+
}
12+
]
13+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"activeLearningEnabled": false,
3+
"answers": [
4+
{
5+
"questions": [
6+
"Q1"
7+
],
8+
"answer": "A1",
9+
"score": 80,
10+
"id": 15,
11+
"source": "Editorial",
12+
"metadata": [
13+
{
14+
"name": "topic",
15+
"value": "value"
16+
}
17+
]
18+
},
19+
{
20+
"questions": [
21+
"Q2"
22+
],
23+
"answer": "A2",
24+
"score": 78,
25+
"id": 16,
26+
"source": "Editorial",
27+
"metadata": [
28+
{
29+
"name": "topic",
30+
"value": "value"
31+
}
32+
]
33+
}
34+
]
35+
}

libraries/botbuilder-ai/tests/qna/test_qna.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,38 @@ async def test_should_filter_low_score_variation(self):
702702
"Should have 3 filtered answers after low score variation.",
703703
)
704704

705+
async def test_should_answer_with_is_test_true(self):
706+
options = QnAMakerOptions(top=1, is_test=True)
707+
qna = QnAMaker(QnaApplicationTest.tests_endpoint)
708+
question: str = "Q11"
709+
context = QnaApplicationTest._get_context(question, TestAdapter())
710+
response_json = QnaApplicationTest._get_json_for_file(
711+
"QnaMaker_IsTest_true.json"
712+
)
713+
714+
with patch(
715+
"aiohttp.ClientSession.post",
716+
return_value=aiounittest.futurized(response_json),
717+
):
718+
results = await qna.get_answers(context, options=options)
719+
self.assertEqual(0, len(results), "Should have received zero answer.")
720+
721+
async def test_should_answer_with_ranker_type_question_only(self):
722+
options = QnAMakerOptions(top=1, ranker_type="QuestionOnly")
723+
qna = QnAMaker(QnaApplicationTest.tests_endpoint)
724+
question: str = "Q11"
725+
context = QnaApplicationTest._get_context(question, TestAdapter())
726+
response_json = QnaApplicationTest._get_json_for_file(
727+
"QnaMaker_RankerType_QuestionOnly.json"
728+
)
729+
730+
with patch(
731+
"aiohttp.ClientSession.post",
732+
return_value=aiounittest.futurized(response_json),
733+
):
734+
results = await qna.get_answers(context, options=options)
735+
self.assertEqual(2, len(results), "Should have received two answers.")
736+
705737
async def test_should_answer_with_prompts(self):
706738
options = QnAMakerOptions(top=2)
707739
qna = QnAMaker(QnaApplicationTest.tests_endpoint, options)

libraries/botbuilder-core/botbuilder/core/activity_handler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ async def on_turn(self, turn_context: TurnContext):
3333
elif turn_context.activity.type == ActivityTypes.event:
3434
await self.on_event_activity(turn_context)
3535
elif turn_context.activity.type == ActivityTypes.end_of_conversation:
36-
await self.on_end_of_conversation(turn_context)
36+
await self.on_end_of_conversation_activity(turn_context)
3737
else:
3838
await self.on_unrecognized_activity_type(turn_context)
3939

@@ -106,7 +106,7 @@ async def on_event( # pylint: disable=unused-argument
106106
):
107107
return
108108

109-
async def on_end_of_conversation( # pylint: disable=unused-argument
109+
async def on_end_of_conversation_activity( # pylint: disable=unused-argument
110110
self, turn_context: TurnContext
111111
):
112112
return

libraries/botbuilder-core/botbuilder/core/auto_save_state_middleware.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
14
from typing import Awaitable, Callable, List, Union
25

36
from .bot_state import BotState

libraries/botbuilder-core/botbuilder/core/bot_framework_adapter.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from .bot_adapter import BotAdapter
3939
from .turn_context import TurnContext
4040
from .user_token_provider import UserTokenProvider
41+
from .invoke_response import InvokeResponse
4142
from .conversation_reference_extension import get_continuation_activity
4243

4344
USER_AGENT = f"Microsoft-BotFramework/3.1 (BotBuilder Python/{__version__})"
@@ -263,7 +264,17 @@ async def process_activity(self, req, auth_header: str, logic: Callable):
263264
teams_channel_data["tenant"]["id"]
264265
)
265266

266-
return await self.run_pipeline(context, logic)
267+
await self.run_pipeline(context, logic)
268+
269+
if activity.type == ActivityTypes.invoke:
270+
invoke_response = context.turn_state.get(
271+
BotFrameworkAdapter._INVOKE_RESPONSE_KEY # pylint: disable=protected-access
272+
)
273+
if invoke_response is None:
274+
return InvokeResponse(status=501)
275+
return invoke_response.value
276+
277+
return None
267278

268279
async def authenticate_request(
269280
self, request: Activity, auth_header: str
@@ -283,7 +294,7 @@ async def authenticate_request(
283294
)
284295

285296
if not claims.is_authenticated:
286-
raise Exception("Unauthorized Access. Request is not authorized")
297+
raise PermissionError("Unauthorized Access. Request is not authorized")
287298

288299
return claims
289300

libraries/botbuilder-core/botbuilder/core/bot_state_set.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
14
from asyncio import wait
25
from typing import List
36
from .bot_state import BotState

libraries/botbuilder-core/botbuilder/core/integration/aiohttp_channel_service_exception_middleware.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
14
from aiohttp.web import (
25
middleware,
36
HTTPNotImplemented,

libraries/botbuilder-core/botbuilder/core/private_conversation_state.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
14
from .bot_state import BotState
25
from .turn_context import TurnContext
36
from .storage import Storage

libraries/botbuilder-core/botbuilder/core/show_typing_middleware.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
14
import time
25
from functools import wraps
36
from typing import Awaitable, Callable

libraries/botbuilder-core/botbuilder/core/teams/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from .teams_activity_handler import TeamsActivityHandler
99
from .teams_info import TeamsInfo
10-
from .teams_helper import deserializer_helper
10+
from .teams_helper import deserializer_helper, serializer_helper
1111
from .teams_activity_extensions import (
1212
teams_get_channel_id,
1313
teams_get_team_info,
@@ -21,4 +21,5 @@
2121
"teams_get_channel_id",
2222
"teams_get_team_info",
2323
"teams_notify_user",
24+
"serializer_helper",
2425
]

libraries/botbuilder-core/botbuilder/core/teams/teams_activity_extensions.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
14
from botbuilder.schema import Activity
25
from botbuilder.schema.teams import NotificationInfo, TeamsChannelData, TeamInfo
36

47

5-
def dummy():
6-
return 1
7-
8-
98
def teams_get_channel_id(activity: Activity) -> str:
109
if not activity:
1110
return None

0 commit comments

Comments
 (0)