forked from cltl/pepper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathresponder.py
233 lines (156 loc) · 7.96 KB
/
responder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
from __future__ import unicode_literals
from pepper.framework import *
from pepper.responder import *
from pepper.framework.component.subtitles import SubtitlesComponent
from pepper import config
from pepper.knowledge import sentences
import numpy as np
from typing import List, Callable
from random import choice
from time import time
import os
RESPONDERS = [
BrainResponder(),
VisionResponder(), PreviousUtteranceResponder(), IdentityResponder(), LocationResponder(), TimeResponder(),
QnAResponder(),
GreetingResponder(), GoodbyeResponder(), ThanksResponder(), AffirmationResponder(), NegationResponder(),
WikipediaResponder(),
WolframResponder(),
UnknownResponder(),
]
class ResponderApp(AbstractApplication, StatisticsComponent,
SubtitlesComponent,
DisplayComponent, SceneComponent, # TODO: (un)comment to turn Web View On/Off
ExploreComponent,
ContextComponent, BrainComponent, SpeechRecognitionComponent,
ObjectDetectionComponent, FaceRecognitionComponent, TextToSpeechComponent):
pass
class DefaultIntention(AbstractIntention, ResponderApp):
IGNORE_TIMEOUT = 60
def __init__(self, application):
super(DefaultIntention, self).__init__(application)
self._ignored_people = {}
self.response_picker = ResponsePicker(self, RESPONDERS + [MeetIntentionResponder()])
def on_chat_enter(self, name):
self._ignored_people = {n: t for n, t in self._ignored_people.items() if time() - t < self.IGNORE_TIMEOUT}
if name not in self._ignored_people:
self.context.start_chat(name)
self.say("{}, {}".format(choice(sentences.GREETING), name))
def on_chat_exit(self):
self.say("{}, {}".format(choice(sentences.GOODBYE), self.context.chat.speaker))
self.context.stop_chat()
def on_chat_turn(self, utterance):
super(DefaultIntention, self).on_chat_turn(utterance)
responder = self.response_picker.respond(utterance)
if isinstance(responder, MeetIntentionResponder):
MeetIntention(self.application)
elif isinstance(responder, GoodbyeResponder):
self._ignored_people[utterance.chat.speaker] = time()
self.context.stop_chat()
# TODO: What are you thinking about? -> Well, Bram, I thought....
class BinaryQuestionIntention(AbstractIntention, ResponderApp):
NEGATION = NegationResponder
AFFIRMATION = AffirmationResponder
def __init__(self, application, question, callback, responders):
# type: (AbstractApplication, List[str], Callable[[bool], None], List[Responder]) -> None
super(BinaryQuestionIntention, self).__init__(application)
self.question = question
self.callback = callback
# Add Necessary Responders if not already included
for responder_class in [self.NEGATION, self.AFFIRMATION]:
if not responder_class in [responder.__class__ for responder in responders]:
responders.append(responder_class())
self.response_picker = ResponsePicker(self, responders)
self.say(choice(question))
def on_chat_turn(self, utterance):
responder = self.response_picker.respond(utterance)
if isinstance(responder, self.AFFIRMATION):
self.callback(True)
elif isinstance(responder, self.NEGATION):
self.callback(False)
else:
self.say(choice(self.question))
class MeetIntention(AbstractIntention, ResponderApp):
CUES = ["my name is", "i am", "no my name is", "no i am"]
def __init__(self, application):
super(MeetIntention, self).__init__(application)
self.response_picker = ResponsePicker(self, RESPONDERS)
self._asrs = [SynchronousGoogleASR(language) for language in ['nl-NL', 'es-ES']]
self._last_statement_was_name = False
self._current_name = None
self._possible_names = {}
self._denied_names = set()
self.context.start_chat("Stranger")
self.say("{} {}".format(choice(sentences.INTRODUCE), choice(sentences.ASK_NAME)))
def on_chat_exit(self):
self.context.stop_chat()
DefaultIntention(self.application)
def on_transcript(self, hypotheses, audio):
self._last_statement_was_name = False
if self._is_name_statement(hypotheses):
# Parse Audio using Multiple Languages!
for asr in self._asrs:
hypotheses.extend(asr.transcribe(audio))
for hypothesis in hypotheses:
self._last_statement_was_name = True
name = hypothesis.transcript.split()[-1]
# If not already denied
if name not in self._denied_names and name[0].isupper():
# Update possible names with this name
if name not in self._possible_names:
self._possible_names[name] = 0.0
self._possible_names[name] += hypothesis.confidence
self._current_name = self._get_current_name()
# If hypotheses about just mentioned name exist -> Ask Verification
if self._last_statement_was_name and self._current_name:
self.say(choice(sentences.VERIFY_NAME).format(self._current_name))
def on_chat_turn(self, utterance):
# If not already responded to Name Utterance
if not self._last_statement_was_name:
# Respond Normally to Whatever Utterance
responder = self.response_picker.respond(utterance)
if self._current_name: # If currently verifying a name
# If negated, remove name from name hypotheses (and suggest alternative)
if isinstance(responder, NegationResponder):
self._denied_names.add(self._current_name)
self._possible_names.pop(self._current_name)
self._current_name = self._get_current_name()
# Try to Verify next best hypothesis
self.say(choice(sentences.VERIFY_NAME).format(self._current_name))
# If confirmed, store name and start chat with person
elif isinstance(responder, AffirmationResponder):
self.say(choice(sentences.JUST_MET).format(self._current_name))
# Save New Person to Memory
self._save()
# Start new chat and switch intention
self.context.start_chat(self._current_name)
DefaultIntention(self.application)
# Exit on User Goodbye
elif isinstance(responder, GoodbyeResponder):
DefaultIntention(self.application)
else: # If some other question was asked, remind human of intention
self.say(choice(sentences.VERIFY_NAME).format(self._current_name))
else: # If no name hypothesis yet exists
self.say("But, {}".format(choice(sentences.ASK_NAME)))
def _get_current_name(self):
if self._possible_names:
return [n for n, c in sorted(self._possible_names.items(), key=lambda i: i[1], reverse=True)][0]
def _is_name_statement(self, hypotheses):
for hypothesis in hypotheses:
for cue in self.CUES:
if cue in hypothesis.transcript.lower():
return True
return False
def _save(self):
name, features = self._current_name, np.concatenate(self.face_vectors).reshape(-1, OpenFace.FEATURE_DIM)
if name != "NEW": # Prevent Overwrite of NEW.bin
self.face_classifier.add(name, features)
features.tofile(os.path.join(config.PEOPLE_NEW_ROOT, "{}.bin".format(name)))
if __name__ == '__main__':
while True:
# Boot Application
application = ResponderApp(config.get_backend())
# Boot Default Intention
intention = DefaultIntention(application)
# Run Application
application.run()