10000 Merge pull request #19 from stevengum97/IntentRecognizerMiddleware · openvelora/botbuilder-python@c84df24 · GitHub
[go: up one dir, main page]

Skip to content

Commit c84df24

Browse files
authored
Merge pull request microsoft#19 from stevengum97/IntentRecognizerMiddleware
add middleware namespace, IntentRecognizerMiddleware, Intent and BotAssert; restructure
2 parents 8544e7e + 6141a6b commit c84df24

File tree

6 files changed

+200
-12
lines changed

6 files changed

+200
-12
lines changed

libraries/botbuilder/microsoft/botbuilder/__init__.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff l 10000 ine numberDiff line change
@@ -5,5 +5,22 @@
55
# license information.
66
# --------------------------------------------------------------------------
77

8-
__all__ = ['activity', 'activity_adapter', 'middleware', 'middleware_set']
8+
9+
from .activity import ACTIVITY_TYPES, ATTACHMENT_LAYOUTS, END_OF_CONVERSATION_CODES, TEXT_FORMATS
10+
from .activity_adapter import ActivityAdapter
11+
from .card_styler import CardStyler, ContentTypes
12+
from .assertions import BotAssert
13+
14+
__all__ = ['activity',
15+
'activity_adapter',
16+
'assertions',
17+
'card_styler',
18+
'ACTIVITY_TYPES',
19+
'ATTACHMENT_LAYOUTS',
20+
'END_OF_CONVERSATION_CODES',
21+
'TEXT_FORMATS',
22+
'ActivityAdapter',
23+
'CardStyler',
24+
'ContentTypes',
25+
'BotAssert']
926

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from typing import *
5+
6+
from microsoft.botbuilder.schema import *
7+
8+
9+
class BotAssert(object):
10+
11+
@staticmethod
12+
def activity_not_none(activity: Activity):
13+
if activity is None:
14+
raise TypeError()
15+
16+
@staticmethod
17+
def context_not_none(context):
18+
if context is None:
19+
raise TypeError()
20+
21+
@staticmethod
22+
def conversation_reference_not_none(reference):
23+
if reference is None:
24+
raise TypeError()
25+
26+
@staticmethod
27+
def adapter_not_null(adapter):
28+
if adapter is None:
29+
raise TypeError()
30+
31+
@staticmethod
32+
def activity_list_not_null(activity_list: List[Activity]):
33+
if activity_list is None:
34+
raise TypeError()
35+
36+
@staticmethod
37+
def middleware_not_null(middleware):
38+
if middleware is None:
39+
raise TypeError()
40+
41+
@staticmethod
42+
def middleware_set_not_null(middleware: List[object]): # object should be Middleware
43+
if middleware is None:
44+
raise TypeError()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# coding=utf-8
2+
# --------------------------------------------------------------------------
3+
# Copyright (c) Microsoft Corporation. All rights reserved.
4+
# Licensed under the MIT License. See License.txt in the project root for
5+
# license information.
6+
# --------------------------------------------------------------------------
7+
8+
from .intent_recognizer_middleware import Intent, IntentRecognizerMiddleware
9+
from .middleware import Middleware
10+
from .middleware_set import MiddlewareSet
11+
12+
__all__ = ['Intent',
13+
'IntentRecognizerMiddleware',
14+
'Middleware',
15+
'MiddlewareSet']
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
5+
import asyncio
6+
from abc import *
7+
from typing import *
8+
9+
from ..assertions import BotAssert
10+
11+
12+
class Intent(object):
13+
def __init__(self, name: str=None, score: float=None, entities: List[object]=None):
14+
self.name = name
15+
self.score = score
16+
self.entities = entities
17+
18+
19+
@abstractmethod
20+
def intent_disabler(context): pass
21+
22+
23+
@abstractmethod
24+
def intent_recognizer(context): pass
25+
26+
27+
@abstractmethod
28+
def intent_result_mutators(context, intents: List[Intent]): pass
29+
30+
31+
class IntentRecognizerMiddleware(object):
32+
def __init__(self):
33+
self._intent_disablers: List[intent_disabler] = []
34+
self._intent_recognizers: List[intent_recognizer] = []
35+
self._intent_result_mutators: List[intent_result_mutators] = []
36+
self._loop = asyncio.get_event_loop()
37+
38+
async def receive_activity(self, context):
39+
BotAssert.context_not_none(context)
40+
41+
intents: List[Intent] = await self.recognize(context)
42+
if len(intents) > 0:
43+
top_intent: Intent = IntentRecognizerMiddleware.find_top_intent(intents)
44+
if top_intent.score > 0.0:
45+
context.top_intent = top_intent
46+
47+
async def recognize(self, context):
48+
BotAssert.context_not_none(context)
49+
is_enabled: bool = await self.is_recognizer_enabled(context)
50+
if is_enabled:
51+
all_recognized_intents: List[Intent] = await self.run_recognizer(context)
52+
await self.run_filters(context, all_recognized_intents)
53+
return all_recognized_intents
54+
else:
55+
return []
56+
57+
async def run_recognizer(self, context):
58+
all_recognized_intents: List[Intent] = []
59+
for recognizer in self._intent_recognizers:
60+
intents: List[Intent] = await recognizer(context)
61+
if intents is not None and len(intents) > 0:
62+
all_recognized_intents.extend(intents)
63+
return all_recognized_intents
64+
65+
async def is_recognizer_enabled(self, context):
66+
for user_code in self._intent_disablers:
67+
is_enabled: bool = await user_code(context)
68+
if is_enabled is False:
69+
return False
70+
return True
71+
72+
async def run_filters(self, context, intents: List[Intent]):
73+
for filter in self._intent_result_mutators:
74+
await filter(context, intents)
75+
76+
def on_enabled(self, pre_condition: intent_disabler):
77+
if pre_condition is None:
78+
raise TypeError()
79+
elif callable(pre_condition) is False:
80+
raise ValueError()
81+
self._intent_disablers.append(pre_condition)
82+
return self
83+
84+
def on_recognize(self, recognizer: intent_recognizer):
85+
if recognizer is None:
86+
raise TypeError()
87+
elif callable(recognizer) is False:
88+
raise ValueError()
89+
self._intent_recognizers.append(recognizer)
90+
return self
91+
92+
def on_filter(self, post_condition: intent_result_mutators):
93+
if post_condition is None:
94+
raise TypeError()
95+
elif callable(post_condition) is False:
96+
raise ValueError()
97+
self._intent_result_mutators.insert(0, post_condition)
98+
return self
99+
100+
@staticmethod
101+
def find_top_intent(intents: List[Intent]):
102+
if intents is None:
103+
raise TypeError()
104+
if len(intents) <= 0:
105+
raise ValueError('No Intents found.')
106+
top_intent = intents[0]
107+
top_score = top_intent.score
108+
for intent in intents:
109+
if intent.score - top_score > 0:
110+
top_score = intent.score
111+
top_intent = intent
112+
return top_intent

libraries/botbuilder/microsoft/botbuilder/middleware.py renamed to libraries/botbuilder/microsoft/botbuilder/middleware/middleware.py

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

44

5-
from abc import ABCMeta, abstractmethod
5+
from abc import ABC, abstractmethod
66

77

8-
class Middleware(ABCMeta):
8+
class Middleware(ABC):
99
"""
1010
It is not required to implement this class as MiddlewareSet also features support for dicts.
1111
When implementing this class you will either need to implement each method.
1212
To avoid having having use each method, either create a dict or override the method to pass.
1313
"""
1414
@abstractmethod
15-
async def context_created(cls, context):
15+
async def context_created(self, context):
1616
"""
1717
(Optional) called when a new context object has been created.
1818
Plugins can extend the context object in this call.
@@ -22,9 +22,9 @@ async def context_created(cls, context):
2222
pass
2323

2424
@abstractmethod
25-
async def receive_activity(cls, context):
25+
async def receive_activity(self, context):
2626
"""
27-
(Optional) called after context_created to route a receive activtiy.
27+
(Optional) called after context_created to route a receive activity.
2828
Plugins can return `{'handled': True}` to indicate that they have successfully routed the
2929
activity. This will prevent further calls to receive_activity()
3030
:param context:
@@ -33,7 +33,7 @@ async def receive_activity(cls, context):
3333
pass
3434

3535
@abstractmethod
36-
async def post_activity(cls, context, activities):
36+
async def send_activity(self, context, activities):
3737
"""
3838
(Optional) called anytime an outgoing set of activities are being sent.
3939
Plugins can implement logic to either transform the outgoing activities or to persist some

libraries/botbuilder/microsoft/botbuilder/middleware_set.py renamed to libraries/botbuilder/microsoft/botbuilder/middleware/middleware_set.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ async def call_middleware(middleware_set):
4949
await asyncio.ensure_future(middleware['receive_activity'](context))
5050
return await asyncio.ensure_future(call_middleware(self._middleware[0:]))
5151

52-
async def post_activity(self, context, activities):
52+
async def send_activity(self, context, activities):
5353
async def call_middleware(middleware_set):
5454
for middleware in middleware_set:
55-
if hasattr(middleware, 'post_activity') and callable(middleware.post_activity):
56-
await asyncio.ensure_future(middleware.post_activity(context, activities))
57-
elif 'post_activity' in middleware and callable(middleware['post_activity']):
58-
await asyncio.ensure_future(middleware['post_activity'](context, activities))
55+
if hasattr(middleware, 'send_activity') and callable(middleware.send_activity):
56+
await asyncio.ensure_future(middleware.send_activity(context, activities))
57+
elif 'send_activity' in middleware and callable(middleware['send_activity']):
58+
await asyncio.ensure_future(middleware['send_activity'](context, activities))
5959
return await asyncio.ensure_future(call_middleware(self._middleware[0:]))

0 commit comments

Comments
 (0)
0