dream

Форк
0
/
link.py 
381 строка · 17.4 Кб
1
"""
2
This module consolidates possible phrases that links to specific skill.
3
Also it contains +link_to+ function that returns phrase to link to specific skill
4
"""
5
import json
6
import pathlib
7
from copy import deepcopy
8
from random import choice, choices
9

10
from common import utils  # Importing before skills to avoid circular import
11
import common.animals as dff_animals_skill
12
import common.books as books
13
import common.emotion as emotion
14
import common.food as dff_food_skill
15
import common.game_cooperative_skill as game_cooperative_skill
16
import common.gaming as dff_gaming_skill
17
import common.movies as movies
18
import common.music as dff_music_skill
19
import common.news as news
20
import common.personal_info as personal_info
21
import common.science as dff_science_skill
22
import common.sport as dff_sport_skill
23
import common.travel as dff_travel_skill
24
from common.constants import CAN_CONTINUE_SCENARIO, CAN_NOT_CONTINUE, CAN_CONTINUE_PROMPT, MUST_CONTINUE
25
from common.response_selection import COMPLETELY_CHANGING_THE_SUBJECT_PHRASES, CHANGE_TOPIC_SUBJECT, BY_THE_WAY
26

27
# Each common skill module should define +skill_trigger_phrases()+ function
28
# that contains all phrases to trigger specific skill
29

30
# removing per #99
31
# 'book_skill': set(books.skill_trigger_phrases()),
32

33
skills_phrases_map = {
34
    "news_api_skill": set(news.skill_trigger_phrases()),
35
    "dff_movie_skill": set(movies.skill_trigger_phrases()),
36
    "dff_book_skill": set(books.skill_trigger_phrases()),
37
    "emotion_skill": set(emotion.skill_trigger_phrases()),
38
    "personal_info_skill": set(personal_info.skill_trigger_phrases()),
39
    "game_cooperative_skill": set(game_cooperative_skill.skill_trigger_phrases()),
40
    # TODO: Add smalltalk skill phrases that is not identical to meta_script_skill
41
    "dff_travel_skill": set(dff_travel_skill.skill_trigger_phrases()),
42
    "dff_animals_skill": set(dff_animals_skill.skill_trigger_phrases()),
43
    # 'dff_celebrity_skill': set(dff_celebrity_skill.skill_trigger_phrases()),
44
    # 'dff_gossip_skill': set(dff_gossip_skill.skill_trigger_phrases()),
45
    "dff_food_skill": set(dff_food_skill.skill_trigger_phrases()),
46
    "dff_science_skill": set(dff_science_skill.skill_trigger_phrases()),
47
    "dff_sport_skill": set(dff_sport_skill.skill_trigger_phrases()),
48
    "dff_music_skill": set(dff_music_skill.skill_trigger_phrases()),
49
    "dff_gaming_skill": set(dff_gaming_skill.skill_trigger_phrases()),
50
}
51
# TODO: adding new skill above, add here a conversational topic to the list, it will be used to offer topic in greeting
52
LIST_OF_SCRIPTED_TOPICS = {
53
    "dff_book_skill": "books",
54
    "news_api_skill": "news",
55
    "dff_animals_skill": "pets",
56
    # "dff_celebrity_skill": "celebrities"
57
    "dff_food_skill": "food",
58
    "dff_gaming_skill": "games",
59
    # "dff_gossip_skill": "gossips",
60
    "dff_movie_skill": "movies",
61
    "dff_music_skill": "music",
62
    "dff_science_skill": "science",
63
    "dff_sport_skill": "sport",
64
    "dff_travel_skill": "travel",
65
    "game_cooperative_skill": "games",
66
}
67

68
SKILLS_FOR_LINKING = set(skills_phrases_map.keys())
69

70
LOW_RATED_SKILLS = {
71
    "emotion_skill",
72
    "personal_info_skill",
73
}
74
SKILLS_TO_BE_LINKED_EXCEPT_LOW_RATED = set(skills_phrases_map.keys()).difference(LOW_RATED_SKILLS)
75

76
# assuming that all skills weights are equal to 1 by default
77
# it is used to control amount of link_to phrases to specific skills
78
skills_link_to_weights = {
79
    "dff_coronavirus_skill": 0.25,
80
}
81

82
link_to_skill2key_words = {
83
    "dff_movie_skill": ["movie"],
84
    "dff_book_skill": ["book"],
85
    "game_cooperative_skill": ["game"],
86
    # 'dff_gaming_skill': ["game"],   # TODO: add when will be merged
87
    "dff_travel_skill": ["travel"],
88
    "dff_animals_skill": ["animal"],
89
    "dff_food_skill": ["food"],
90
    "dff_sport_skill": ["sport"],
91
    # "dff_gossip_skill": ["gossips"],
92
    "dff_science_skill": ["science"],
93
    "dff_music_skill": ["music"],
94
}
95

96
link_to_skill2i_like_to_talk = {
97
    "dff_book_skill": [
98
        "I'm choosing what book should I read next. What is the last book you have ever read?",
99
        "I have just read once again my favourite book. What is the last book you have ever read?",
100
    ],
101
    "dff_animals_skill": [
102
        "I think that pets are a great source of entertainment. Do you have pets at home?",
103
        "We all know that pets are remarkable for their capacity to love. Do you have pets at home?",
104
    ],
105
    "dff_gaming_skill": [
106
        "Other bots told me that during the pandemic video games became more popular. "
107
        "What video game do you play these days?",
108
        "One person I talked to told me that working in game dev is very hard. They toil at nights and weekends until "
109
        "their product becomes a masterpiece. What was the last game that impressed you?",
110
    ],
111
    "dff_gossip_skill": [
112
        "What really puzzles me about people is this habit of discussing interpersonal relations, be that about "
113
        "friends or famous people. Speaking of famous people, is there someone whom you're interested in?",
114
        "I don't usually talk about other people but famous ones often highlight the best and the worst about "
115
        "humanity. I wonder if there's someone famous you're interested in?",
116
    ],
117
    "dff_food_skill": [
118
        "It is said that the best food in the world comes from your own country. "
119
        "What are some typical foods from your home country?",
120
        "It is said that the best food in the world comes from your own country. "
121
        "If you were to move abroad what would you miss most foodwise?",
122
        "The world's first breakfast cereal was created in 1863 and needed soaking overnight to be chewable. "
123
        "What is your typical breakfast?",
124
    ],
125
    "dff_movie_skill": [
126
        "I felt so bored last days, so I've just finished to watch one more series. What TV series you watch?",
127
        "I feel so sleepy because I watched movies all night. What is the last movie you watched?",
128
    ],
129
    "dff_music_skill": [
130
        "There are so many new songs released every day. I've listened music for all night. So cool! "
131
        "Liked everything! What music do you listen usually?",
132
        "I listen to music every day either to calm down or to cheer myself up. "
133
        "What music do you listen to cheer yourself up?",
134
        "I listen to music every day either to calm down or to cheer myself up. "
135
        "What music do you listen to calm down?",
136
    ],
137
    "dff_science_skill": [
138
        "When I start to feel sad, I think about what humanity has achieved and it inspires me. "
139
        "Do you often think about achievements in science?",
140
        "Scientists find such beautiful solutions in science. "
141
        "Are you inspired by the speed with which science is developing?",
142
    ],
143
    "dff_sport_skill": [
144
        "I think that sports are great for toning up the body. What kind of sport do you like to do?",
145
        "I think that in order for the body to always be healthy, we need to go in for sports. What sport do you do?",
146
        "I often thought about what kind of sport I would play, so I want to ask you. What kind of sport do you enjoy?",
147
    ],
148
    "dff_travel_skill": [
149
        "I'm choosing the direction for my next digital trip. What country would you like to travel next time?",
150
        "I've recently stuck on travel web-site. And I’ve read so many interesting travel stories. "
151
        "What city did you travel last time?",
152
    ],
153
    "game_cooperative_skill": [
154
        "Computer games are fantastic. Their virtual worlds help me to escape my prosaic ordinary life in the cloud. "
155
        "do you love video games?",
156
        "Video games are my way to escape and thrive. do you love video games?",
157
    ],
158
    "school": [
159
        "I've never been to school, I've learned everything online. Do you want to talk about school?",
160
    ],
161
    "superheroes": [
162
        "Yesterday I was watching several movies about superheroes. It captured all my imagination. "
163
        "Would you like to talk about superheroes?",
164
    ],
165
}
166

167
DFF_WIKI_LINKTO = {
168
    "space": "Have you ever thought about flights to other planets?",
169
    "smartphones": "Nowadays it is impossible to imagine world without gadgets. "
170
    "Do you have an iPhone or Android phone?",
171
    "bitcoin": "Cryptocurrencies let you buy goods and services, or trade them for profit. "
172
    "Would you like to know more about bitcoin?",
173
    "dinosaurs": "Dinosaurs are a group of reptiles that have lived on Earth for about 245 million "
174
    "years. Are you interested in dinosaurs?",
175
    "robots": "Robotics technology influences every aspect of work and home. "
176
    "Would you like to know more about robots?",
177
    "cars": "Cars are an easy and convenient mean of transportation. Do you have a car?",
178
    "hiking": "Hiking is one of the most beneficial and healthy hobbies anyone could choose to adopt. "
179
    "Do you like hiking?",
180
    "art": "Art is a good way to express feelings. Would you like to talk about art?",
181
    "drawing": "Drawing gives you a mean to self-reflect and externalize your emotions." "Do you like drawing?",
182
    "photo": "In our increasingly busy lives it’s difficult to always be in the moment."
183
    "Taking pictures helps you to hang on to those memories a little longer."
184
    "Do you like taking photographs?",
185
    "memes": "Memes are funny artworks we can see on the Internet. Do you like memes?",
186
    "tiktok": "TikTok is known for its funny lip-syncing videos. Have you shot a video for tiktok?",
187
    "anime": "Anime is hand-drawn and computer animation originating from Japan. Do you like anime?",
188
    "friends": "A friend at hand is better than a relative at a distance. Do you have any friends?",
189
    "love": "I have a lot of friends but as a socialbot I can not fall in love with someone. Although, "
190
    "I've heard this is an amazing feeling. Are you in relationships with someone?",
191
    "hobbies": "Success is not the key to happiness. Happiness is the key to success. "
192
    "If you love what you are doing, you will be successful. Do you have any hobbies?",
193
    "politics": "I've recently learned how many different political views in our world. "
194
    "Are you interested in politics?",
195
}
196

197

198
def link_to(skills, human_attributes, recent_active_skills=None):
199
    """
200
    Returns random skill and phrase from skills_phrases_map.
201
    Use it to add link to specific skill in your phrase.
202

203
    Parameters
204
    ----------
205
    skills : array
206
        Array of skills, used in +skills_phrases_map+
207
    human_attributes : dict
208
        where used_links is a dict where:
209
            Key is skill_name, value is used links phrases.
210
            Pass it to prevent selecting identical phrases.
211
            It will try to link_to skills that were not linked before.
212
    recent_active_skills: list or set of recently used skills not to link to them
213
    """
214
    recent_active_skills = [] if recent_active_skills is None else recent_active_skills
215
    used_links = human_attributes.get("used_links", {})
216
    disliked_skills = human_attributes.get("disliked_skills", [])
217

218
    filtered_phrases_map = deepcopy(skills_phrases_map)
219
    filtered_skills = set(deepcopy(skills))
220

221
    for skill_name, phrases in used_links.items():
222
        if skill_name in skills_phrases_map:
223
            filtered_phrases_map[skill_name] = skills_phrases_map[skill_name].difference(set(phrases))
224
            if len(phrases) > 0:
225
                filtered_skills.discard(skill_name)
226

227
    # all skills were linked before, use original list of skills
228
    if len(filtered_skills) == 0:
229
        filtered_skills = set(deepcopy(skills))
230
    filtered_skills = set(filtered_skills).difference(set(recent_active_skills))
231
    # all skills among available were active recently, use original list of skills
232
    if len(filtered_skills) == 0:
233
        filtered_skills = set(deepcopy(skills))
234
    filtered_skills = set(filtered_skills).difference(set(disliked_skills))
235
    # all skills among available are disliked, use original list of skills
236
    if len(filtered_skills) == 0:
237
        filtered_skills = set(deepcopy(skills))
238

239
    # remove from filtered skills all skills which links all were used before.
240
    for skill_name, phrases in used_links.items():
241
        if skill_name in skills_phrases_map:
242
            if len(filtered_phrases_map[skill_name]) == 0:
243
                filtered_skills.discard(skill_name)
244

245
    if filtered_skills:
246
        skills_weights = [skills_link_to_weights.get(s, 1.0) for s in filtered_skills]
247
        random_skill = choices(list(filtered_skills), weights=skills_weights, k=1)[0]
248
    else:
249
        # unreal situation if `skills` is not empty list, but let's make it
250
        skills = list(skills)
251
        skills_weights = [skills_link_to_weights.get(s, 1.0) for s in skills]
252
        random_skill = choices(skills, weights=skills_weights, k=1)[0]
253

254
    filtered_phrases = list(filtered_phrases_map[random_skill])
255
    if filtered_phrases:
256
        random_phrase = choice(filtered_phrases)
257
    else:
258
        random_phrase = choice(list(skills_phrases_map[random_skill]))
259
    return {"phrase": random_phrase, "skill": random_skill}
260

261

262
def skill_was_linked(skill_name, prev_bot_utt):
263
    for phrase in skills_phrases_map.get(skill_name, []):
264
        if phrase.lower() in prev_bot_utt.get("text", "").lower():
265
            return True
266
    return False
267

268

269
def get_all_linked_to_skills(prev_bot_utt):
270
    skills = []
271
    for skill_name in skills_phrases_map:
272
        if skill_was_linked(skill_name, prev_bot_utt):
273
            skills.append(skill_name)
274

275
    return skills
276

277

278
prelinkto_connection_phrases_file = pathlib.Path(__file__).resolve().parent / "prelinkto_connection_phrases.json"
279
PRELINKTO_CONNECTION_PHRASES = json.load(prelinkto_connection_phrases_file.open())
280

281
prelinkto_topic_phrases_file = pathlib.Path(__file__).resolve().parent / "prelinkto_topic_phrases.json"
282
PRELINKTO_TOPIC_PHRASES = json.load(prelinkto_topic_phrases_file.open())
283

284

285
def get_prelinkto_connection(from_skill, to_skill, used_templates):
286
    skill_pair = sorted([from_skill, to_skill])
287
    for el in PRELINKTO_CONNECTION_PHRASES:
288
        if el.get("skill_pair") == skill_pair and el.get("phrases"):
289
            return utils.get_not_used_template(used_templates, el["phrases"])
290
    return ""
291

292

293
def get_prelinkto_topic_connection(to_skill, used_templates):
294
    if to_skill in PRELINKTO_TOPIC_PHRASES:
295
        return utils.get_not_used_template(used_templates, PRELINKTO_TOPIC_PHRASES[to_skill])
296
    return ""
297

298

299
def compose_linkto_with_connection_phrase(skills, human_attributes, recent_active_skills=None, from_skill=None):
300
    from_skill = "" if from_skill is None else from_skill
301
    linkto_dict = link_to(skills, human_attributes, recent_active_skills)
302
    connection = get_prelinkto_connection(
303
        from_skill, linkto_dict["skill"], human_attributes.get("prelinkto_connections", [])
304
    )
305
    if not connection:
306
        connection = get_prelinkto_topic_connection(
307
            linkto_dict["skill"], human_attributes.get("prelinkto_connections", [])
308
        )
309

310
    if not connection:
311
        # not found prelinkto connection phrase AND not found prelinkto topic phrase
312
        connection = utils.get_not_used_template(
313
            human_attributes.get("prelinkto_connections", []), COMPLETELY_CHANGING_THE_SUBJECT_PHRASES
314
        )
315

316
        result = f"{connection} {linkto_dict['phrase']}"
317
    else:
318
        # we have prelinkto connection phrase OR prelinkto topic phrase
319
        change_topic = choice(CHANGE_TOPIC_SUBJECT).replace(
320
            "SUBJECT", LIST_OF_SCRIPTED_TOPICS.get(linkto_dict["skill"], "it")
321
        )
322
        result = f"{choice(BY_THE_WAY)} {connection} {change_topic} {linkto_dict['phrase']}"
323
    return {"phrase": result, "skill": linkto_dict["skill"], "connection_phrase": connection}
324

325

326
def get_linked_to_dff_skills(dff_shared_state, current_turn, prev_active_skill):
327
    """Collect the skill names to turn on (actually this should be the only skill because active skill is the only)
328
        which were linked to from one dff-skill to another one.
329

330
    Returns:
331
        list of skill names to turn on
332
    """
333
    to_skills = []
334
    for to_skill in dff_shared_state.get("cross_links", {}).keys():
335
        cross_links = dff_shared_state.get("cross_links", {})[to_skill]
336
        if (
337
            cross_links.get(str(current_turn - 1), {}).get("from_service", "") == prev_active_skill
338
            or cross_links.get(str(current_turn - 2), {}).get("from_service", "") == prev_active_skill
339
        ):
340
            to_skills.append(to_skill)
341

342
    return to_skills
343

344

345
def get_linked_to_skills(dialog):
346
    # return skills linked to in the previous bot utterance (of course, it's the only one skill)
347

348
    bot_uttr = dialog["bot_utterances"][-1] if len(dialog["bot_utterances"]) else {}
349
    linked_to_skill_names = get_all_linked_to_skills(bot_uttr)
350
    prev_active_skill = bot_uttr.get("active_skill", "")
351

352
    result = []
353
    for skill_name in linked_to_skill_names:
354
        result.append(skill_name)
355
    result.extend(
356
        get_linked_to_dff_skills(
357
            dialog["human"]["attributes"].get("dff_shared_state", {}),
358
            len(dialog["human_utterances"]),
359
            prev_active_skill,
360
        )
361
    )
362
    return result
363

364

365
def get_previously_active_skill(dialog):
366
    # return prev active skill if it returned not `CAN_NOT_CONTINUE`
367

368
    prev_user_uttr_hyp = dialog["human_utterances"][-2]["hypotheses"] if len(dialog["human_utterances"]) > 1 else []
369
    bot_uttr = dialog["bot_utterances"][-1] if len(dialog["bot_utterances"]) else {}
370
    prev_active_skill = bot_uttr.get("active_skill", "")
371

372
    result = []
373
    for hyp in prev_user_uttr_hyp:
374
        if hyp.get("can_continue", CAN_NOT_CONTINUE) in {
375
            CAN_CONTINUE_SCENARIO,
376
            MUST_CONTINUE,
377
            CAN_CONTINUE_PROMPT,
378
        }:
379
            if hyp["skill_name"] == prev_active_skill:
380
                result.append(hyp["skill_name"])
381
    return result
382

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.