Compare commits

...

44 Commits

Author SHA1 Message Date
kb
e2e447a601 Testy skadnikow i ilosci 2024-06-13 18:10:19 +02:00
kb
0b9a15488c Testy skadnikow i ilosci 2024-06-13 18:03:58 +02:00
kb
9c528afc6c Testy skadnikow i ilosci 2024-06-13 17:59:02 +02:00
kb
05876dbd24 Testy skadnikow i ilosci 2024-06-13 17:46:54 +02:00
kb
bf309fb6fe Testy skadnikow i ilosci 2024-06-13 17:44:59 +02:00
kb
73b9e52d31 Silence 2024-06-13 17:11:04 +02:00
kb
78f01cb165 zmiana stylu rozmowy na bardziej naturalny 2024-06-13 17:08:43 +02:00
kb
3316917d47 Silence 2024-06-13 17:00:16 +02:00
kb
e42ae14ceb Silence 2024-06-13 16:59:22 +02:00
kb
fefc6bd7bc Finalne poprawki 2024-06-13 16:56:47 +02:00
kb
437f5f7d72 Poprawka 2024-06-13 16:49:21 +02:00
kb
a759108c9e Poprawki 2024-06-13 16:48:54 +02:00
kb
d1108d9b8a Poprawka buga 2024-06-13 16:40:21 +02:00
kb
75fb0f873b Dopytujemy o kompletne zamówienie 2024-06-13 16:36:17 +02:00
kb
02312cc3b9 Testing 2024-06-13 16:11:22 +02:00
kb
118099bb37 Tests 2024-06-13 15:59:31 +02:00
kb
24f75db1d8 Merge branch 'master' of https://git.wmi.amu.edu.pl/s495726/chat-restaruacja 2024-06-13 15:52:37 +02:00
kb
ac5dcdc06d Merge branch 'master' of https://git.wmi.amu.edu.pl/s495726/chat-restaruacja 2024-06-13 15:52:27 +02:00
kb
ad0b4cf739 Merge branch 'master' of https://git.wmi.amu.edu.pl/s495726/chat-restaruacja 2024-06-13 15:50:18 +02:00
kb
3fb60acd4f Test 2024-06-13 15:49:31 +02:00
kb
0fda2dbb98 Test 2024-06-13 15:39:50 +02:00
kb
35a19cc5c0 Fix merge conflicts 2024-06-13 15:33:23 +02:00
kb
4ce52466a2 Merge 2024-06-13 15:30:23 +02:00
kb
10fc617cad Poprawki do systemu, złożenie wszystkich modułów w całość 2024-06-13 15:25:52 +02:00
41f41cc692 Update src/main.py 2024-06-12 16:02:39 +02:00
e7a8814438 templatki 2024-06-12 16:00:40 +02:00
fded76596b NLG 2024-06-12 16:00:02 +02:00
5f128e7e88 Zmiana staga po dodaniu określonego wpisu do DSM 2024-06-10 23:35:45 +02:00
dfed26e955 Zmiany dla dialog policy 2024-06-10 20:01:24 +02:00
kb
a928c925fc Update readme 2024-06-10 19:24:04 +02:00
kb
809079cd56 Poprawki do main.py 2024-06-10 18:43:43 +02:00
kb
4620e53229 Poprawki 2024-06-10 18:20:11 +02:00
f7ee0ba059 Changes for dialog_policy 2024-06-10 17:48:55 +02:00
kb
f4d9bff809 Dodaje opcje do mockowania NLU 2024-06-10 17:44:48 +02:00
kb
ff0b4af87c Poprawki - usuwamy convlaba 2024-06-10 17:37:59 +02:00
166dd4886c Add dialog policy 2024-06-10 02:58:56 +02:00
a850690dc9 Formatowanie 2024-06-10 00:54:09 +02:00
2b6a8b01a6 Dodanie obsługi napoi w DSM 2024-06-10 00:53:38 +02:00
79bf27aca4 Poprawka testu 2024-06-10 00:48:05 +02:00
3d820e8d6d Usunięcie sprawdzania poprawności zamówienia 2024-06-10 00:46:52 +02:00
abc098e498 Dodanie sprawdzania istnienia pizzy i liczenia totala 2024-06-10 00:39:31 +02:00
a90535a68b Dodanie stanów i ograniczeń do DSM 2024-06-10 00:19:13 +02:00
4483747021 Usunięcie convlaba z DSM 2024-06-09 23:05:07 +02:00
kb
caea45471c Przygotowania pod convlaba 2024-06-09 21:29:38 +02:00
12 changed files with 886 additions and 357 deletions

View File

@ -40,7 +40,14 @@ Agent powinien wykazywać elastyczność, adaptując się do potrzeb klienta, np
- Python 3.10.12
- Instalacja dependencies `pip3 install -r requirements.txt`
- Centralna część systemu - uruchamiamy `python3 src/main.py`
- Centralna część systemu - uruchamiamy `python3 src/main.py` - wymagane są wyuczone modele (patrz niżej)
- NLU:
- uczenie modeli od zera `python3 nlu_train.py`
- Ewaluacja `python3 evaluate.py`
# Gotowe modele NLU
- [frame-model-prod](https://1drv.ms/f/s!Ar75ftQiNIxxhcgPS1EOLu0zC_WWzg?e=tJRqbB)
- [slot-model-prod](https://1drv.ms/f/s!Ar75ftQiNIxxhcgb2X6pFioRxXHVew?e=ZC6LFI)
Nazwa folderów jest istotna - muszą byc odpowiednio `frame-model-prod` i `slot-model-prod` oraz znajdować się w głównym katalogu repozytorium.

View File

@ -1,51 +1,92 @@
{
"dough": ["thick"],
"drink": ["pepsi", "cola", "water"],
"food": ["pizza"],
"meat": ["chicken", "ham", "tuna"],
"sauce": ["garlic", "1000w"],
"ingredient": {
"chicken": {},
"tuna": {},
"pineapple": {},
"onion": {},
"cheese": {},
"tomato": {},
"ham": {},
"pepper": {}
"size": ["M", "L", "XL"],
"dough": [
"thick"
],
"drink": {
"woda": {
"price": 5
},
"menu": ["capri", "margarita", "hawajska", "barcelona", "tuna"],
"pizza": {
"capri": {
"ingredient": ["tomato", "ham", "mushrooms", "cheese"],
"price": 25
},
"margarita": {
"ingredient": ["tomato", "cheese"],
"price": 20
},
"hawajska": {
"ingredient": ["tomato", "pineapple", "chicken", "cheese"],
"price": 30
},
"barcelona": {
"ingredient": ["tomato", "onion", "ham", "pepper", "cheese"],
"price": 40
},
"tuna": {
"ingredient": ["tomato", "tuna", "onion", "cheese"],
"price": 40
}
"pepsi": {
"price": 7
},
"size": {
"m": {
"price_multiplier": 1
},
"l": {
"price_multiplier": 1.2
},
"xl": {
"price_multiplier": 1.4
}
"cola": {
"price": 8
}
},
"food": [
"pizza"
],
"meat": [
"kurczak",
"szynka",
"tuna"
],
"sauce": [
"garlic",
"1000w"
],
"ingredients": {
"kurczak": {},
"tuna": {},
"ananas": {},
"cebula": {},
"ser": {},
"pomidor": {},
"szynka": {},
"papryka": {}
},
"menu": [
"capri",
"margarita",
"hawajska",
"barcelona",
"tuna"
],
"pizza": {
"capri": {
"ingredient": [
"tomato",
"ham",
"mushrooms",
"cheese"
],
"price": 25
},
"margarita": {
"ingredient": [
"tomato",
"cheese"
],
"price": 20
},
"hawajska": {
"ingredient": [
"tomato",
"pineapple",
"chicken",
"cheese"
],
"price": 30
},
"barcelona": {
"ingredient": [
"tomato",
"onion",
"ham",
"pepper",
"cheese"
],
"price": 40
},
"tuna": {
"ingredient": [
"tomato",
"tuna",
"onion",
"cheese"
],
"price": 40
}
}
}

View File

@ -2,5 +2,6 @@ flair==0.13.1
conllu==4.5.3
pandas==1.5.3
numpy==1.26.4
torch==2.3.0
convlab==3.0.2a0
torch==1.13
convlab==3.0.2a0
scipy==1.12

View File

@ -1,40 +1,56 @@
from service.dialog_state_monitor import DialogStateMonitor
from service.dialog_policy import DialogPolicy
from service.natural_languag_understanding import NaturalLanguageUnderstanding
from service.natural_language_generation import NaturalLanguageGeneration, parse_frame
from service.templates import templates
# initialize classes
nlu = NaturalLanguageUnderstanding() # NLU
monitor = DialogStateMonitor() # DSM
dialog_policy = DialogPolicy() # DP
language_generation = NaturalLanguageGeneration(templates) # NLG
# Main loop
user_input = input("Możesz zacząć pisać.\n")
while True:
# NLU
frame = nlu.process_input(user_input)
# print(frame)
# DSM
monitor.update(frame)
# DP
# print(dialog_policy.next_dialogue_act(monitor.read()).act)
# NLG
act, slots = parse_frame(frame)
response = language_generation.generate(act, slots)
print(response)
if frame.act == "bye":
break
user_input = input(">\n")
from service.dialog_state_monitor import DialogStateMonitor
from service.dialog_policy import DialogPolicy
from service.natural_languag_understanding import NaturalLanguageUnderstanding
from service.natural_language_generation import NaturalLanguageGeneration, parse_frame
from service.templates import templates
# initialize classes
nlu = NaturalLanguageUnderstanding(use_mocks=False) # NLU
monitor = DialogStateMonitor() # DSM
dialog_policy = DialogPolicy() # DP
language_generation = NaturalLanguageGeneration(templates) # NLG
def frame_to_dict(frame):
return {
"act": frame.act,
"slots": [{"name": slot.name, "value": slot.value} for slot in frame.slots],
"act_understood": frame.act_understood,
}
# Main loop
dial_num = 0
print("CTRL+C aby zakończyć program.")
while True:
monitor.reset()
print(f"\n==== Rozpoczynasz rozmowę nr {dial_num} ====\n")
user_input = input("Witamy w naszej pizza-przez-internet. W czym mogę pomóc?\n")
while True:
# NLU
frame = nlu.predict(user_input)
# print("Frame: ", frame)
# DSM
monitor.update(frame)
# DP
system_action = dialog_policy.predict(monitor)
system_action_dict = frame_to_dict(system_action) # Ensure system_action is a dictionary
# print("System action: ", system_action_dict)
# NLG
response = language_generation.generate(frame, system_action_dict)
print(response)
if system_action.act == "bye_and_thanks" or system_action.act == "bye":
monitor.print_order()
break
if frame.act == "bye":
print(monitor.print_order())
break
user_input = input(">\n")
dial_num += 1

View File

@ -1,10 +1,11 @@
from .slot import Slot
class Frame:
def __init__(self, source: str, act: str, slots: list[Slot]):
def __init__(self, source: str, act: str, slots: list[Slot] = [], act_understood = None):
self.source = source
self.slots = slots
self.act = act
self.act_understood = act_understood
def __str__(self):
msg = f"Act: {self.act}, Slots: ["

View File

@ -1,8 +1,66 @@
from collections import defaultdict
from model.frame import Frame
from model.slot import Slot
class DialogPolicy:
def next_dialogue_act(self, frames: list[Frame]) -> Frame:
if frames[-1].act == "welcomemsg":
return Frame("system", "welcomemsg", [])
else:
return Frame("system", "canthelp", [])
def _predict(self, dsm):
last_frame = dsm.state['history'][-1]
act_processed = dsm.state['was_system_act_processed']
if(dsm.state['was_previous_order_invalid'] == False):
if last_frame.act == "inform/order-complete":
act = last_frame.act
elif ("inform" in last_frame.act):
act = last_frame.act.split('/')[0]
else:
act = last_frame.act
match(act):
case "bye":
return Frame(source="system", act = "bye")
case "inform/order-complete":
return Frame(source="system", act = "bye_and_thanks")
case "inform" | "affirm" | "negate":
current_active_stage = dsm.get_current_active_stage()
if current_active_stage == None:
return Frame(source="system", act = "bye_and_thanks")
match(current_active_stage['name']):
case "collect_food":
return Frame(source="system", act = "request/food", slots = [Slot("menu", dsm.state['constants']['menu'])], act_understood=act_processed)
case "collect_drinks":
return Frame(source="system", act = "request/drinks", slots = [Slot("drink", dsm.state['constants']['drink'])], act_understood=act_processed)
case "more_food":
return Frame(source="system", act = "request/food-more", slots = [Slot("menu", dsm.state['constants']['menu'])], act_understood=act_processed)
case "more_drinks":
return Frame(source="system", act = "request/drinks-more", slots = [Slot("drink", dsm.state['constants']['drink'])], act_understood=act_processed)
case "collect_address":
return Frame(source="system", act = "request/address", act_understood=act_processed)
case "collect_payment_method":
return Frame(source="system", act = "request/payment-method", act_understood=act_processed)
case "collect_phone":
return Frame(source="system", act = "request/phone", act_understood=act_processed)
case "request/menu":
return Frame(source="system", act = "inform", slots = [Slot("menu", dsm.state['constants']['menu'])])
case "request/price":
return Frame(source="system", act = "inform", slots = [Slot("price", dsm.state['total_cost'])])
case "request/ingredients":
return Frame(source="system", act = "inform", slots = [Slot("ingredients", dsm.state['constants']['ingredients'])])
case "request/sauce":
return Frame(source="system", act = "inform", slots = [Slot("sauce", dsm.state['constants']['sauce'])])
case "request/time":
return Frame(source="system", act = "inform", slots = [Slot("time", dsm.state['belief_state']['time'])])
case "request/size":
return Frame(source="system", act = "inform", slots = [Slot("size", dsm.state['constants']['size'])])
case "request/delivery-price":
return Frame(source="system", act = "inform", slots = [Slot("delivery-price", "10")])
case "request/drinks":
return Frame(source="system", act = "inform", slots = [Slot("drink", dsm.state['constants']['drink'])])
case "welcomemsg":
return Frame(source="system", act = "inform", slots = [Slot("menu", dsm.state['constants']['menu'])])
case "repeat":
return Frame(source="system", act = "repeat")
return Frame(source="system", act = "repeat")
def predict(self, dsm):
frame = self._predict(dsm)
dsm.state["system_history"].append(frame)
return frame

View File

@ -1,55 +1,183 @@
from src.model.frame import Frame
from convlab.dst.dst import DST
from model.frame import Frame
import copy
import json
def normalize(value):
value = value.lower()
if value[-1] in [",", "!", "?" ,"." ,")" ,"(",")"]:
value = value[:-1]
if value[-2:] == "ie":
value = value[:-2] + "a"
if value[-1] in ["ę", "ą", "y"]:
value = value[:-1] + "a"
if value in ["pizz", "pizzy", "pizze", "picce"]:
value = "pizza"
return ' '.join(value.split())
class DialogStateMonitor(DST):
domain = 'restaurant'
def __init__(self):
DST.__init__(self)
self.__initial_state = dict(user_action=[],
system_action=[],
belief_state={
'order': [],
'address': {},
'order-complete': False,
'phone': {},
'delivery': {},
'payment': {},
'time': {},
'name': {},
},
booked={},
request_state={},
terminated=False,
history=[])
class DialogStateMonitor:
def __init__(self, initial_state_file: str = 'attributes.json'):
with open(initial_state_file) as file:
constants = json.load(file)
self.__initial_state = dict(
belief_state={
'order': [],
'address': {},
'order-complete': False,
'phone': {},
'delivery': {},
'payment': {},
'time': {},
'name': {},
},
total_cost=0,
stages=[
{'completed': False, 'name': 'collect_food'},
{'completed': False, 'name': 'more_food'},
{'completed': False, 'name': 'collect_drinks'},
{'completed': False, 'name': 'more_drinks'},
{'completed': False, 'name': 'collect_address'},
{'completed': False, 'name': 'collect_phone'},
],
was_previous_order_invalid=False,
was_system_act_processed=False,
constants=constants,
history=[],
system_history=[],
)
self.state = copy.deepcopy(self.__initial_state)
def update(self, frame: Frame):
def get_current_active_stage(self) -> str | None:
for stage in self.state['stages']:
if stage['completed'] is False:
# print("Current stage: ", stage['name'])
return stage
self.state['belief_state']['order-complete'] = True
def mark_current_stage_completed(self) -> None:
for stage in self.state['stages']:
if stage['completed'] is False:
# print("Stage completed: ", stage['name'])
stage['completed'] = True
return
def complete_stage_if_valid(self, stage_name):
for stage in self.state['stages']:
if stage['name'] != stage_name:
continue
if stage['name'] == "collect_food":
for order in self.state['belief_state']['order']:
if order.get("pizza"):
stage['completed'] = True
return
elif stage["name"] == "collect_drinks":
for order in self.state['belief_state']['order']:
if order.get("drink"):
stage['completed'] = True
return
elif stage["name"] == "collect_address":
if not len(self.state['belief_state']['address']):
return
stage['completed'] = True
return
elif stage["name"] == "collect_phone":
if self.state['belief_state']["phone"].get("phone"):
stage['completed'] = True
return
pass
def item_exists(self, type: str, name: str) -> bool:
return normalize(name) in self.state['constants'][type]
def drink_exists(self, name: str) -> bool:
return normalize(name) in self.state['constants']['pizza']
def get_total_cost(self) -> int:
return self.state['total_cost']
def slot_augmentation(self, slot, value):
drink_normalize = ["woda", "pepsi", "cola", "coca cola", "cole", "coca"]
if slot.name in ["food", "pizza", "ingredient"]:
if value in drink_normalize:
slot.name = 'drink'
return slot
def slot_valid(self, slot, act):
if act == "inform/order":
if slot.name in ["address", "payment-method", 'delivery', 'phone', 'time', 'name']:
return False
return True
def value_valid(self, slot_name, value):
if slot_name == "food":
if value != "pizza":
return False
return True
def update(self, frame: Frame) -> None:
self.state['was_system_act_processed'] = False
belief_state_copy = copy.deepcopy(self.state["belief_state"])
self.state['history'].append(frame)
if frame.source != 'user':
return
if frame.act == 'inform/order':
new_order = dict()
for slot in frame.slots:
new_order[slot.name] = normalize(slot.value)
self.state['belief_state']['order'].append(new_order)
value = normalize(slot.value)
slot = self.slot_augmentation(slot, value)
if not self.slot_valid(slot, frame.act):
continue
if not self.value_valid(slot.name, value):
continue
stage_name = self.get_current_active_stage()['name']
is_collect_food = (slot.name == 'pizza' and stage_name == 'collect_food')
is_more_food = (slot.name == 'pizza' and stage_name == 'more_food')
is_collect_drinks = (slot.name == 'drink' and stage_name == 'collect_drinks')
is_more_drinks = (slot.name == 'drink' and stage_name == 'more_drinks')
if is_collect_food or is_collect_drinks or is_more_food or stage_name == is_more_drinks:
if self.item_exists(slot.name, value) is False:
self.state['was_previous_order_invalid'] = True
return
self.state['was_previous_order_invalid'] = False
self.state['total_cost'] += self.state['constants'][slot.name][value]['price']
if slot.name == "pizza":
if new_order.get("pizza"):
new_order[slot.name].append({"name": value, "ingredient": [], "ingredient/neg": []})
else:
new_order[slot.name] = [{"name": value, "ingredient": [], "ingredient/neg": []}]
elif slot.name == "drink":
if new_order.get("drink"):
new_order[slot.name].append({"drink": value})
else:
new_order[slot.name] = [{"drink": value}]
elif slot.name in ["ingredient", "ingredient/neg"]:
pizzas_list = new_order.get("pizza")
if pizzas_list:
pizzas_list[-1][slot.name].append(value)
if len(new_order) > 0:
self.state['belief_state']['order'].append(new_order)
self.complete_stage_if_valid('collect_food')
self.complete_stage_if_valid('collect_drinks')
elif frame.act == 'inform/address':
for slot in frame.slots:
self.state['belief_state']['address'][slot.name] = normalize(slot.value)
self.complete_stage_if_valid('collect_address')
elif frame.act == 'inform/phone':
for slot in frame.slots:
self.state['belief_state']['phone'][slot.name] = normalize(slot.value)
self.complete_stage_if_valid('collect_phone')
elif frame.act == 'inform/order-complete':
self.state['belief_state']['order-complete'] = True
elif frame.act == 'inform/delivery':
for slot in frame.slots:
self.state['belief_state']['delivery'][slot.name] = normalize(slot.value)
self.complete_stage_if_valid('collect_address')
elif frame.act == 'inform/payment':
for slot in frame.slots:
self.state['belief_state']['payment'][slot.name] = normalize(slot.value)
@ -59,6 +187,23 @@ class DialogStateMonitor(DST):
elif frame.act == 'inform/name':
for slot in frame.slots:
self.state['belief_state']['name'][slot.name] = normalize(slot.value)
elif frame.act == 'negate':
if "request" in self.state["system_history"][-1].act:
self.mark_current_stage_completed()
self.state['was_system_act_processed'] = True
def reset(self):
if self.state["belief_state"] != belief_state_copy and frame.act not in ["repeat", 'affirm', 'negate']:
self.state['was_system_act_processed'] = True
def reset(self) -> None:
self.state = copy.deepcopy(self.__initial_state)
def print_order(self) -> dict:
print("\n=== Oto podsumowanie Twojego zamówienia ===")
for o in self.state['belief_state']['order']:
print(o)
print("Adres dostawy: ")
print(self.state['belief_state']['address'])
print("Numer telefonu: ")
print(self.state['belief_state']['phone'])
print(f"Czy zostało pomyślnie zrealizowane: {self.state['belief_state']['order-complete']}")

View File

@ -1,7 +1,6 @@
from flair.models import SequenceTagger
from utils.nlu_utils import predict_single, predict_and_annotate
from model.frame import Frame, Slot
import random
"""
ACTS:
inform/order
@ -41,8 +40,13 @@ SLOTS:
sauce
"""
class NaturalLanguageUnderstanding:
def __init__(self):
class NaturalLanguageUnderstanding():
def __init__(self, use_mocks=False):
self.use_mocks = use_mocks
if self.use_mocks:
return
from flair.models import SequenceTagger
print("\n========================================================")
print("Models are loading, it may take a moment, please wait...")
print("========================================================\n")
@ -85,8 +89,20 @@ class NaturalLanguageUnderstanding:
return slots
def process_input(self, text: str):
act = self.__predict_intention(text)
slots = self.__predict_slot(text)
frame = Frame(source = 'user', act = act, slots = slots)
return frame
def predict(self, text: str):
if not self.use_mocks:
try:
act = self.__predict_intention(text)
slots = self.__predict_slot(text)
frame = Frame(source = 'user', act = act, slots = slots)
return frame
except:
return Frame(source="user", act = "repeat", slots=[])
else:
frames = [
Frame(source="user", act = "inform/order", slots=[Slot(name="pizza", value="barcelona")]),
Frame(source="user", act = "welcomemsg", slots=[]),
Frame(source="user", act = "request/menu", slots=[]),
]
return random.choice(frames)

View File

@ -1,24 +1,43 @@
import re
from service.template_selector import select_template
import random
# from service.templates import templates
def parse_frame(frame):
if not hasattr(frame, 'act') or not hasattr(frame, 'slots'):
raise TypeError("Expected a Frame object with 'act' and 'slots' attributes.")
act = frame.act
slots = [{"name": slot.name, "value": slot.value} for slot in frame.slots]
return act, slots
class NaturalLanguageGeneration:
def __init__(self, templates):
self.templates = templates
def generate(self, act, slots):
template = select_template(act, slots)
if template == "default/template":
template = random.choice(self.templates["default/template"])
slot_dict = {slot['name']: slot['value'] for slot in slots}
return template.format(**slot_dict)
import re
from service.template_selector import select_template
import random
class NaturalLanguageGeneration:
def __init__(self, templates):
self.templates = templates
def generate(self, frame, system_action):
# Parsowanie frame
act, slots = parse_frame(frame)
# Wybierz szablon na podstawie system_action
template = select_template(system_action['act'], system_action['slots'])
if template is None:
print(f"Brak szablonu dla act: {system_action['act']} z slotami: {system_action['slots']}")
template = random.choice(self.templates["default/template"])
# Zamień sloty na wartości
slot_dict = {}
for slot in system_action['slots']:
if isinstance(slot['value'], list):
slot_dict[slot['name']] = ', '.join(slot['value'])
elif isinstance(slot['value'], dict):
slot_dict[slot['name']] = ', '.join([f"{k}: {v}" for k, v in slot['value'].items()])
else:
slot_dict[slot['name']] = slot['value']
response = template.format(**slot_dict)
if system_action['act_understood'] == True:
response = f"Zrozumiałem. {response}"
elif system_action['act_understood'] == False:
response = f"Nie zrozumiałem. {response}"
return response
def parse_frame(frame):
if not hasattr(frame, 'act') or not hasattr(frame, 'slots'):
raise TypeError("Expected a Frame object with 'act' and 'slots' attributes.")
act = frame.act
slots = [{"name": slot.name, "value": slot.value} for slot in frame.slots]
return act, slots

View File

@ -1,35 +1,216 @@
import random
from service.templates import templates
def select_template(act, slots):
slot_names = {slot['name'] for slot in slots}
if act == "welcomemsg":
return random.choice(templates["welcomemsg"])
if act == "request/menu":
return random.choice(templates["request/menu"])
if act == "inform/address":
return random.choice(templates["inform/address"])
if act == "inform/delivery":
return random.choice(templates["inform/delivery"])
if act == "inform/payment":
return random.choice(templates["inform/payment"])
if act == "affirm":
return random.choice(templates["affirm"])
if act == "request/drinks":
return random.choice(templates["request/drinks"])
if act == "bye":
return random.choice(templates["bye"])
if act == "inform/order":
if "quantity" in slot_names and "food" in slot_names and "pizza" in slot_names:
return templates["inform/order"][1]
elif "quantity" in slot_names and "pizza" in slot_names:
return templates["inform/order"][4]
elif "food" in slot_names and "pizza" in slot_names:
return templates["inform/order"][2]
elif "quantity" in slot_names:
return templates["inform/order"][3]
else:
return templates["inform/order"][4]
return "default/template"
import random
from service.templates import templates
def generate_pizza_info(slots):
pizza_name = None
query_type = None
for slot in slots:
if slot['name'] == 'pizza':
pizza_name = slot['value'].lower()
elif slot['name'] == 'ingredient':
query_type = 'ingredient'
elif slot['name'] == 'price':
query_type = 'price'
if pizza_name not in pizza_data:
return f"Nie mamy w ofercie pizzy o nazwie {pizza_name}."
if query_type == 'ingredient':
ingredients = pizza_data[pizza_name]['ingredient']
return f"Składniki pizzy {pizza_name} to: {', '.join(ingredients)}."
elif query_type == 'price':
price = pizza_data[pizza_name]['price']
return f"Cena pizzy {pizza_name} to {price} zł."
return f"Informacje o pizzy {pizza_name}: składniki to {', '.join(pizza_data[pizza_name]['ingredient'])}, cena to {pizza_data[pizza_name]['price']} zł."
def generate_ingredients_response(slots):
ingredients = [slot['value'] for slot in slots if slot['name'] == 'ingredients']
if ingredients:
ingredient_list = []
for ingredient in ingredients:
ingredient_list.extend(ingredient.keys())
response = f"Dostępne składniki to: {', '.join(ingredient_list)}."
return response
return "Nie podano składników."
def generate_drinks_response(slots, act):
more = 'drinks-more' in act
drinks = [slot['value'] for slot in slots if slot['name'] == 'drink']
if drinks:
drink_details = []
for drink in drinks:
for name, details in drink.items():
# price = details.get('price', 'unknown')
drink_details.append(f"{name}") # w cenie {price} zł")
if not more:
if len(drink_details) > 1:
response = f"Czy chcesz coś do picia? Dostępne napoje to: {', '.join(drink_details[:-1])} oraz {drink_details[-1]}."
else:
response = f"Czy chcesz coś do picia? Dostępne napoje to: {drink_details[0]}."
return response
else:
if len(drink_details) > 1:
response = f"Czy chcesz coś jeszcze picia?"
else:
response = f"Czy chcesz coś jeszcze picia?"
return response
return "Nie podano napojów."
def generate_size_response(slots):
sizes = [slot['value'] for slot in slots if slot['name'] == 'size'][0]
if len(sizes) != 0:
return F"Dostępne rozmiary to: {', '.join(sizes)}"
return "Nie podano rozmiarów."
def generate_sauce_response(slots):
sauces = [slot['value'] for slot in slots if slot['name'] == 'sauce']
if sauces:
sauce_list = []
for sauce in sauces:
if isinstance(sauce, list):
sauce_list.extend(sauce)
else:
sauce_list.append(sauce)
return f"Dostępne sosy to: {', '.join(sauce_list)}."
return "Nie podano sosów."
def select_template(act, slots):
slot_names = {slot['name'] for slot in slots}
if act == "welcomemsg":
return random.choice(templates["welcomemsg"])
if "ingredients" in slot_names:
return generate_ingredients_response(slots)
elif "drink" in slot_names:
return generate_drinks_response(slots, act)
elif "sauce" in slot_names:
return generate_sauce_response(slots)
elif "size" in slot_names:
return generate_size_response(slots)
elif "price" in slot_names:
return random.choice(templates["inform/price"])
elif "food" in slot_names:
return random.choice(templates["inform/menu"])
if act == "inform":
if "menu" in slot_names:
return random.choice(templates["inform/menu"])
elif "address" in slot_names:
return random.choice(templates["inform/address"])
elif "phone" in slot_names:
return random.choice(templates["inform/phone"])
elif "order-complete" in slot_names:
return random.choice(templates["inform/order-complete"])
elif "delivery" in slot_names:
return random.choice(templates["inform/delivery"])
elif "payment" in slot_names:
return random.choice(templates["inform/payment"])
elif "time" in slot_names:
return random.choice(templates["inform/time"])
elif "name" in slot_names:
return random.choice(templates["inform/name"])
elif "price" in slot_names and "pizza" in slot_names:
return generate_pizza_info(slots)
elif act == "inform/order":
if "quantity" in slot_names and "pizza" in slot_names and "size" in slot_names:
return templates["inform/order"][1]
elif "quantity" in slot_names and "pizza" in slot_names:
return templates["inform/order"][2]
elif "quantity" in slot_names and "food" in slot_names:
return templates["inform/order"][0]
elif "food" in slot_names and "pizza" in slot_names and "price" in slot_names:
return templates["inform/order"][5]
elif "quantity" in slot_names:
return templates["inform/order"][3]
else:
return templates["inform/order"][4]
elif act == "request/menu":
return random.choice(templates["request/menu"])
elif act == "inform/address":
return random.choice(templates["inform/address"])
elif act == "request/price":
return random.choice(templates["request/price"])
elif act == "inform/menu":
return random.choice(templates["inform/menu"])
elif act == "request/ingredients":
return random.choice(templates["request/ingredients"])
elif act == "request/sauce":
return random.choice(templates["request/sauce"])
elif act == "inform/phone":
return random.choice(templates["inform/phone"])
elif act == "inform/order-complete":
return random.choice(templates["inform/order-complete"])
elif act == "request/time":
return random.choice(templates["request/time"])
elif act == "request/size":
return random.choice(templates["request/size"])
elif act == "inform/delivery":
return random.choice(templates["inform/delivery"])
elif act == "inform/payment":
return random.choice(templates["inform/payment"])
elif act == "request/delivery-price":
return random.choice(templates["request/delivery-price"])
elif act == "inform/time":
return random.choice(templates["inform/time"])
elif act == "request/drinks":
return random.choice(templates["request/drinks"])
elif act == "request/food":
return random.choice(templates["inform/menu"])
elif act == "request/food-more":
return random.choice(templates["inform/menu-more"])
elif act == "inform/name":
return random.choice(templates["inform/name"])
elif act == "bye":
return random.choice(templates["bye"]) # TODO force end?
elif act == "bye_and_thanks":
return random.choice(templates["bye_and_thanks"])
elif act == "repeat":
return random.choice(templates["repeat"])
elif act == "request/address":
return random.choice(templates["request/address"])
elif act == "request/payment-method":
return random.choice(templates["request/payment-method"])
elif act == "request/phone":
return random.choice(templates["request/phone"])
return None
# def select_template(act, slots):
# slot_names = {slot['name'] for slot in slots}
# if act == "welcomemsg":
# return random.choice(templates["welcomemsg"])
# if act == "request/menu":
# return random.choice(templates["request/menu"])
# if act == "inform/address":
# return random.choice(templates["inform/address"])
# if act == "inform/delivery":
# return random.choice(templates["inform/delivery"])
# if act == "inform/payment":
# return random.choice(templates["inform/payment"])
# if act == "affirm":
# return random.choice(templates["affirm"])
# if act == "request/drinks":
# return random.choice(templates["request/drinks"])
# if act == "bye":
# return random.choice(templates["bye"])
# if act == "inform/order":
# if "quantity" in slot_names and "food" in slot_names and "pizza" in slot_names:
# return templates["inform/order"][1]
# elif "quantity" in slot_names and "pizza" in slot_names:
# return templates["inform/order"][4]
# elif "food" in slot_names and "pizza" in slot_names:
# return templates["inform/order"][2]
# elif "quantity" in slot_names:
# return templates["inform/order"][3]
# else:
# return templates["inform/order"][4]
# return "default/template"

View File

@ -1,157 +1,169 @@
templates = {
"inform/order": [
"Zamówiłeś {quantity} x {food}.",
"Twoje zamówienie to {quantity} x {food} {pizza}.",
"Mmmm... {food} {pizza} to rewelacyjny wybór. Czy chcesz coś do picia?",
"Dziękujemy za zamówienie {quantity} x {food}.",
"Na jaką pizzę masz ochotę?"
],
"request/menu": [
"Oto nasze menu: {menu}.",
"Nasze menu obejmuje: {menu}.",
"Proszę, oto lista dostępnych dań: {menu}.",
"Dostępne dania to: {menu}.",
"W naszym menu znajdziesz: {menu}."
],
"inform/address": [
"Twój adres to: {address}.",
"Adres, który podałeś, to: {address}.",
"Dostawa na adres: {address}.",
"Dostarczymy na adres: {address}.",
"Twój podany adres to: {address}."
],
"request/price": [
"Cena za {food} wynosi {price} zł.",
"Koszt {food} to {price} zł.",
"Cena {pizza} o rozmiarze {size} wynosi {price} zł.",
"Za {quantity} x {pizza} zapłacisz {price} zł.",
"Koszt {food} to {price} zł."
],
"request/ingredients": [
"Nasza {food} {pizza} składa się z ciasta, sosu pomidorowego, sera i innych dodatków.",
"W {pizza} znajdziesz: {ingredients}.",
"{pizza} zawiera: {ingredients}.",
"W skład {pizza} wchodzi: {ingredients}.",
"Na {pizza} składają się: {ingredients}."
],
"request/sauce": [
"Jakie sosy chciałbyś do {food}?",
"Proszę wybrać sosy do {food}.",
"Jakie sosy zamawiasz do {food}?",
"Które sosy mają być do {food}?",
"Wybierz sosy do {food}."
],
"inform/phone": [
"Twój numer telefonu to: {phone}.",
"Podany numer telefonu: {phone}.",
"Numer telefonu, który podałeś, to: {phone}.",
"Twoje dane kontaktowe: {phone}.",
"Telefon kontaktowy: {phone}."
],
"inform/order-complete": [
"Twoje zamówienie zostało zrealizowane. Dziękujemy!",
"Zamówienie zakończone. Dziękujemy za zakupy!",
"Zamówienie zrealizowane. Czekaj na dostawę!",
"Twoje zamówienie jest gotowe. Dziękujemy!",
"Realizacja zamówienia zakończona. Dziękujemy!"
],
"request/time": [
"Oczekiwany czas dostawy to {time} minut.",
"Dostawa zajmie około {time} minut.",
"Czas dostawy to około {time} minut.",
"Przewidywany czas dostawy to {time} minut.",
"Twoje zamówienie dotrze w {time} minut."
],
"request/size": [
"Jaką wielkość {pizza} preferujesz? Mamy {sizes}.",
"Dostępne rozmiary {pizza} to {sizes}.",
"Jaką wielkość {pizza} chciałbyś zamówić? {sizes}.",
"Proszę wybrać rozmiar {pizza}: {sizes}.",
"Mamy następujące rozmiary {pizza}: {sizes}."
],
"affirm": [
"Świetnie! Napisz co Ci chodzi po głowie.",
"Dobrze! Co dalej?",
"OK! Co chciałbyś zamówić?",
"Super! Co dalej?",
"Dobrze! Jakie dalsze zamówienia?"
],
"inform/delivery": [
"Twoje zamówienie zostanie dostarczone na {address}.",
"Dostarczymy zamówienie na adres: {address}.",
"Dostawa na adres: {address}.",
"Twoje zamówienie jedzie na {address}.",
"Adres dostawy: {address}."
],
"inform/address": [
"Twoje zamówienie zostanie dostarczone na adres: {address}.",
"Dostawa będzie na adres: {address}.",
"Adres dostawy: {address}."
],
"inform/payment": [
"Metoda płatności to: {payment-method}.",
"Płatność realizujesz przez: {payment-method}.",
"Wybrałeś metodę płatności: {payment-method}.",
"Płatność: {payment-method}.",
"Możesz zapłacić kartą, gotówką lub blikiem"
],
"request/delivery-price": [
"Koszt dostawy wynosi {delivery-price} zł.",
"Cena dostawy to {delivery-price} zł.",
"Za dostawę zapłacisz {delivery-price} zł.",
"Dostawa kosztuje {delivery-price} zł.",
"Koszt dostawy: {delivery-price} zł."
],
"inform/time": [
"Aktualny czas to {time}.",
"Jest teraz {time}.",
"Czas: {time}.",
"Godzina: {time}.",
"Obecny czas: {time}."
],
"request/drinks": [
"Jakie napoje chciałbyś zamówić?",
"Proszę wybrać napoje do zamówienia.",
"Jakie napoje dołączamy do zamówienia?",
"Co chciałbyś pić?",
"Proszę podać napoje do zamówienia."
],
"inform/name": [
"Twoje imię to {name}.",
"Podane imię: {name}.",
"Twoje imię: {name}.",
"Masz na imię {name}.",
"Imię: {name}."
],
"welcomemsg": [
"Witaj w naszej wspaniałej pizzerii. W czym mogę pomóc?",
"Halo, halo, tu najlepsza pizza w mieście. Masz głoda?",
"Dzieńdoberek, gdyby wszyscy jedli nasze pizze, na świecie nie byłoby wojen. Jaką pizzę sobie dziś gruchniesz?",
],
"request/menu": [
"W naszym menu znajdują się pizze, spaghetti, gnocci oraz aranchini. Polecam potrawkę śląską po grecku.",
"Smażymy, gotujemy, prażymy, ale najlepiej nam wychodzi pizza. Na co masz ochotę?",
],
"request/drink": [
"Oferujemy napoje zimne, ciepłe i letnie. Cola, fanta, woda mineralna, kawa, herbata lub frappe.",
"Może z alkoholem? Mamy świeżo warzone piwo",
],
"bye": [
"Dziękujemy i do zobaczenia wkrótce.",
"Polecamy się na przyszłość. Do zobaczenia!",
"Do widzenia.",
],
"negate": [
"Przepraszam, nie mamy {ingredient/neg} w ofercie.",
"Niestety, {ingredient/neg} jest niedostępne.",
"Nie posiadamy {ingredient/neg} w menu.",
"Brak {ingredient/neg} w naszej ofercie.",
"Niestety, nie mamy {ingredient/neg}."
],
"default/template": [
"Przepraszamy, ale nie rozumiemy Twojego zapytania.",
"Proszę spróbować ponownie później.",
"Nie rozpoznajemy Twojej prośby, spróbuj ponownie.",
"Strasznie szybko to napisałeś, nie zrozumiałem...."
]
}
templates = {
"inform/order": [
"Zamówiłeś {quantity} x {food}.",
"Twoje zamówienie to {quantity} x {food} {pizza}.",
"Mmmm... {food} {pizza} to rewelacyjny wybór. Czy chcesz coś do picia?",
"Dziękujemy za zamówienie {quantity} x {food}.",
"Na jaką pizzę masz ochotę?"
],
"inform/menu": [
"Oferujemy następujące pizze: {menu}.",
],
"inform/menu-more": [
"Czy chcesz jeszcze jakąś pizzę?",
],
"inform/name": [
"Twoje imię to {name}.",
"Podane imię: {name}.",
"Twoje imię: {name}.",
"Masz na imię {name}.",
"Imię: {name}."
],
"inform/price": [
"Cena wybranej pizzy to {price} zł.",
"Koszt pizzy to {price} zł.",
"Wybrana pizza kosztuje {price} zł.",
"Cena pizzy wynosi {price} zł."
],
"inform/address": [
"Twój adres to: {address}.",
"Adres, który podałeś, to: {address}.",
"Dostawa na adres: {address}.",
"Dostarczymy na adres: {address}.",
"Twój podany adres to: {address}."
],
"inform/sauce": [
"Dostępne sosy to: {sauce}.",
"Możesz wybrać spośród następujących sosów: {sauce}.",
"Oferujemy następujące sosy: {sauce}."
],
"inform/phone": [
"Twój numer telefonu to: {phone}.",
"Podany numer telefonu: {phone}.",
"Numer telefonu, który podałeś, to: {phone}.",
"Twoje dane kontaktowe: {phone}.",
"Telefon kontaktowy: {phone}."
],
"inform/order-complete": [
"Twoje zamówienie zostało zrealizowane. Dziękujemy!",
"Zamówienie zakończone. Dziękujemy za zakupy!",
"Zamówienie zrealizowane. Czekaj na dostawę!",
"Twoje zamówienie jest gotowe. Dziękujemy!",
"Realizacja zamówienia zakończona. Dziękujemy!"
],
"inform/delivery": [
"Twoje zamówienie zostanie dostarczone na {address}.",
"Dostarczymy zamówienie na adres: {address}.",
"Dostawa na adres: {address}.",
"Twoje zamówienie jedzie na {address}.",
"Adres dostawy: {address}."
],
"inform/address": [
"Twoje zamówienie zostanie dostarczone na adres: {address}.",
"Dostawa będzie na adres: {address}.",
"Adres dostawy: {address}."
],
"inform/payment": [
"Metoda płatności to: {payment-method}.",
"Płatność realizujesz przez: {payment-method}.",
"Wybrałeś metodę płatności: {payment-method}.",
"Płatność: {payment-method}.",
"Możesz zapłacić kartą, gotówką lub blikiem"
],
"affirm": [
"Świetnie! Napisz co Ci chodzi po głowie.",
"Dobrze! Co dalej?",
"OK! Co chciałbyś zamówić?",
"Super! Co dalej?",
"Dobrze! Jakie dalsze zamówienia?"
],
"request/delivery-price": [
"Koszt dostawy wynosi {delivery-price} zł.",
"Cena dostawy to {delivery-price} zł.",
"Za dostawę zapłacisz {delivery-price} zł.",
"Dostawa kosztuje {delivery-price} zł.",
"Koszt dostawy: {delivery-price} zł."
],
"request/menu": [
"W naszym menu znajdziesz: {menu}."
],
"inform/time": [
"Aktualny czas to {time}.",
"Jest teraz {time}.",
"Czas: {time}.",
"Godzina: {time}.",
"Obecny czas: {time}."
],
"welcomemsg": [
"Witaj w naszej wspaniałej pizzerii. W czym mogę pomóc?",
"Halo, halo, tu najlepsza pizza w mieście. Masz głoda?",
"Dzieńdoberek, gdyby wszyscy jedli nasze pizze, na świecie nie byłoby wojen. Jaką pizzę sobie dziś gruchniesz?",
],
"request/price": [
"Cena za {food} wynosi {price} zł.",
"Koszt {food} to {price} zł.",
"Cena {pizza} o rozmiarze {size} wynosi {price} zł.",
"Za {quantity} x {pizza} zapłacisz {price} zł.",
"Koszt {food} to {price} zł."
],
"request/ingredients": [
"Nasza {food} {pizza} składa się z ciasta, sosu pomidorowego, sera i innych dodatków.",
"W {pizza} znajdziesz: {ingredients}.",
"{pizza} zawiera: {ingredients}.",
"W skład {pizza} wchodzi: {ingredients}.",
"Na {pizza} składają się: {ingredients}."
],
"request/sauce": [
"Jakie sosy chciałbyś do {food}?",
"Proszę wybrać sosy do {food}.",
"Jakie sosy zamawiasz do {food}?",
"Które sosy mają być do {food}?",
"Wybierz sosy do {food}."
],
"request/food": [
"Co chciałbyś zamówić?",
"Proszę podać na jaką pizzę masz ochotę",
"Którą pizzę wybrałeś?"
],
"request/time": [
"Oczekiwany czas dostawy to {time} minut.",
"Dostawa zajmie około {time} minut.",
"Czas dostawy to około {time} minut.",
"Przewidywany czas dostawy to {time} minut.",
"Twoje zamówienie dotrze w {time} minut."
],
"request/size": [
"Jaką wielkość {pizza} preferujesz? Mamy {sizes}.",
"Dostępne rozmiary {pizza} to {sizes}.",
"Jaką wielkość {pizza} chciałbyś zamówić? {sizes}.",
"Proszę wybrać rozmiar {pizza}: {sizes}.",
"Mamy następujące rozmiary {pizza}: {sizes}."
],
"bye": [
"Dziękujemy i do zobaczenia wkrótce.",
"Polecamy się na przyszłość. Do zobaczenia!",
"Do widzenia.",
],
"negate": [
"Przepraszam, nie mamy {ingredient/neg} w ofercie.",
"Niestety, {ingredient/neg} jest niedostępne.",
"Nie posiadamy {ingredient/neg} w menu.",
"Brak {ingredient/neg} w naszej ofercie.",
"Niestety, nie mamy {ingredient/neg}."
],
"default/template": [
"Nie zrozumiałem, spróbuj inaczej sformułować zdanie."
],
"repeat": [
"Nie zrozumiałem, spróbuj inaczej sformułować zdanie.",
],
"request/address": [
"Jaki adres dostawy?"
],
"bye_and_thanks": [
"Dziękujęmy, przekazaliśmy zamówienie do realizacji. Do zobaczenia!",
],
"request/phone": [
"Podaj proszę numer telefonu do kontaktu dla kuriera."
]
}

View File

@ -2,17 +2,49 @@ from src.service.dialog_state_monitor import DialogStateMonitor
from src.model.frame import Frame
from src.model.slot import Slot
dst = DialogStateMonitor()
dsm = DialogStateMonitor()
dst.update(Frame('user', 'inform/order', [Slot('pizza', 'margaritta'), Slot('sauce', 'ketchup')]))
dst.update(Frame('user', 'inform/order', [Slot('pizza', 'carbonara')]))
dst.update(Frame('user', 'inform/order-complete', []))
assert dsm.item_exists('pizza', 'capri') is True
assert dsm.item_exists('pizza', 'buraczana') is False
assert dsm.item_exists('drink', 'cola') is True
assert dsm.state['was_previous_order_invalid'] is False
assert dst.state['belief_state']['order'][0]['pizza'] == 'margaritta'
assert dst.state['belief_state']['order'][0]['sauce'] == 'ketchup'
assert dst.state['belief_state']['order-complete'] == True
assert dsm.get_current_active_stage() == 'collect_food'
frame1 = Frame('user', 'inform/order', [Slot('pizza', 'margarita'), Slot('sauce', 'ketchup')])
dsm.update(frame1)
assert dsm.get_current_active_stage() == 'collect_drinks'
assert dsm.get_total_cost() == 20
frame2 = Frame('user', 'inform/order', [Slot('pizza', 'tuna')])
dsm.update(frame2)
assert dsm.get_current_active_stage() == 'collect_drinks'
assert dsm.get_total_cost() == 20 # Pizza is not added, as previous stage is closed already
frame3 = Frame('user', 'inform/order-complete', [])
dsm.update(frame3)
frame4 = Frame('user', 'inform/order', [Slot('drink', 'cola')])
dsm.update(frame4)
assert dsm.get_current_active_stage() == 'collect_address'
assert dsm.get_total_cost() == 30
dst.reset()
assert dsm.state['belief_state']['order'][0]['pizza'] == 'margarita'
assert dsm.state['belief_state']['order'][0]['sauce'] == 'ketchup'
assert dsm.state['belief_state']['order-complete'] is True
assert dsm.state['history'][0] == frame1
assert dsm.state['history'][1] == frame2
assert dsm.state['history'][2] == frame3
assert dsm.state['history'][3] == frame4
assert dst.state['belief_state']['order'] == []
assert dst.state['belief_state']['order-complete'] == False
dsm.reset()
assert dsm.get_total_cost() == 0
assert dsm.get_current_active_stage() == 'collect_food'
assert dsm.state['belief_state']['order'] == []
assert dsm.state['belief_state']['order-complete'] is False
assert len(dsm.state['history']) == 0
dsm.reset()
frame1 = Frame('user', 'inform/order', [Slot('pizza', 'buraczana')])
dsm.update(frame1)
assert dsm.state['was_previous_order_invalid'] is True
assert dsm.state['belief_state']['order'] == []
assert dsm.get_total_cost() == 0