feat: add chatbot config and exercise

This commit is contained in:
Mikołaj Gawron 2024-05-07 21:40:14 +02:00
parent f039d6462b
commit 5996ff712a
16 changed files with 147 additions and 73 deletions

13
Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM python:3.12.3
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY chatbot/ chatbot/
WORKDIR /app/chatbot
CMD ["python", "main.py"]

View File

@ -0,0 +1,41 @@
# Systemy dialogowe - sklep internetowy
## Requirements
Docker version 20.\*+
## Installation
Make sure that you Docker is running now.
`docker build -t chatbot .`
There is always possibilit to run it without Docker.
List of the required library we can find inside:
`requirements.txt`
or by:
`pip install requirements.txt`
## Development and usage
We can run script
`sh run.sh`
or
`docker run -it --rm -p 8888:8888 chatbot`
On both cases we are mapping our src directory to workdir inside Docker to always run the newest code.
## Contributors
| Team member |
|----------------|
| Mikołaj Gawron |
| |
| |
| |

View File

@ -1,3 +0,0 @@
{
"data_path": "./data/intents.json"
}

View File

@ -0,0 +1,4 @@
{
"data_path": "./data/intents.json",
"responses_path": "./data/response.json"
}

View File

@ -1,12 +1,12 @@
{
"name_query": [
"jak masz na imię",
"jak się nazywasz",
"twoje imię",
"jak mogę cię wołać",
"jak do ciebie mówić",
"jak cię zwać",
"jak cię nazywać",
"jak masz na imie"
]
"name_query": [
"jak masz na imię",
"jak się nazywasz",
"twoje imię",
"jak mogę cię wołać",
"jak do ciebie mówić",
"jak cię zwać",
"jak cię nazywać",
"jak masz na imie"
]
}

View File

@ -1,26 +1,22 @@
from modules.nlp import NaturalLanguageProcessor, Config
from modules.state_monitor import DialogueStateMonitor
from modules.strategy import DialogueStrategy
from modules.generator import NaturalLanguageGenerator
from pathlib import Path
from modules.nlp import NaturalLanguageProcessor
from modules.generator import ResponseGenerator
from modules.config import Config
import colorama
from colorama import Fore, Style
colorama.init(autoreset=True)
def chatbot_response(input_text: str, nlp: NaturalLanguageProcessor) -> str:
dialogue_monitor = DialogueStateMonitor()
analysis = nlp.analyze(input_text)
dialogue_monitor.update_state(analysis['intent'])
response = DialogueStrategy.decide_response(dialogue_monitor.state)
final_response = NaturalLanguageGenerator.generate(response)
def main():
base_path = Path(__file__).resolve().parent
config_path = base_path / 'config' / 'config.json'
config = Config.load_config(config_path)
return final_response
if __name__ == "__main__":
config = Config()
nlp = NaturalLanguageProcessor(config)
generator = ResponseGenerator(config)
print(Fore.CYAN + "Witaj w chatbocie! Rozpocznij rozmowę.")
print(Fore.YELLOW + "Wpisz 'quit' aby zakończyć program.\n")
while True:
@ -29,4 +25,10 @@ if __name__ == "__main__":
print(Fore.RED + "Zamykanie chatbota...")
break
print(Fore.CYAN + "Bot: " + chatbot_response(user_input, nlp))
intent = nlp.analyze(user_input)
response = generator.generate(intent)
print(Fore.CYAN + "Bot: " + response)
if __name__ == "__main__":
main()

View File

24
chatbot/modules/config.py Normal file
View File

@ -0,0 +1,24 @@
import json
from pathlib import Path
from pydantic import BaseModel, ValidationError
class Config(BaseModel):
data_path: Path
responses_path: Path
@classmethod
def load_config(cls, config_path: Path) -> 'Config':
try:
with config_path.open('r', encoding='utf-8') as config_file:
config_data = json.load(config_file)
return cls(**config_data)
except FileNotFoundError:
print("Config file not found.")
exit(1)
except json.JSONDecodeError:
print("Invalid JSON.")
exit(1)
except ValidationError as e:
print(f"Configuration validation error: {e}")
exit(1)

View File

@ -1,4 +1,13 @@
class NaturalLanguageGenerator:
@staticmethod
def generate(response: str) -> str:
return response
import json
from typing import Dict
from .config import Config
import random
class ResponseGenerator:
def __init__(self, config: Config):
with config.responses_path.open('r', encoding='utf-8') as file:
self.responses: Dict[str, list] = json.load(file)
def generate(self, response_key: str) -> str:
return random.choice(self.responses.get(response_key, ["Przepraszam, nie rozumiem. Możesz to powtórzyć?"]))

View File

@ -1,38 +1,20 @@
import json
import os
from typing import Any, Dict, Literal
from typing import Dict, List, TypedDict
from .config import Config
class Config:
def __init__(self) -> None:
try:
config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'config.json')
with open(config_path, 'r', encoding='utf-8') as config_file:
self.config_data: Dict[str, Any] = json.load(config_file)
except FileNotFoundError:
print("Config file not found.")
self.config_data = {}
except json.JSONDecodeError:
print("Invalid JSON.")
self.config_data = {}
def get_data_path(self) -> str:
data_path = self.config_data.get('data_path', '')
if not isinstance(data_path, str):
raise ValueError("Data path must be a string.")
return os.path.join(os.path.dirname(os.path.dirname(__file__)), data_path)
class Intents(TypedDict):
name_query: List[str]
class NaturalLanguageProcessor:
def __init__(self, config: Config) -> None:
self.config = config
data_path = self.config.get_data_path()
with open(data_path, 'r', encoding='utf-8') as file:
self.intents: Dict[str, Any] = json.load(file)
def __init__(self, config: Config):
with config.data_path.open('r', encoding='utf-8') as file:
self.intents: Intents = json.load(file)
def analyze(self, input_text: str) -> Dict[str, Literal['ask_name', 'unknown']]:
def analyze(self, input_text: str) -> str:
lower_text = input_text.lower()
for phrase in self.intents.get('name_query', []):
if phrase in lower_text:
return {"intent": "ask_name"}
return {"intent": "unknown"}
for intent, phrases in self.intents.items():
if any(phrase in lower_text for phrase in phrases):
return intent
return "unknown"

View File

@ -1,7 +1,8 @@
import random
class DialogueStrategy:
@staticmethod
def decide_response(state: dict) -> str:
if state['last_intent'] == 'ask_name':
return "Witaj, nazywam się Dia."
else:
return "Przepraszam, nie rozumiem. Możesz to powtórzyć?"
def decide_response(state: dict, responses) -> str:
intent_responses = responses.get(state['last_intent'], ["Przepraszam, nie rozumiem. Możesz to powtórzyć?"])
return random.choice(intent_responses)

View File

@ -2,7 +2,7 @@ import os
import re
import pandas as pd
import numpy as np
from chatbot.modules.nlu import NLU
from modules.nlu import NLU
rows = 0
hits = 0

0
modules/__init__.py Normal file
View File

View File

@ -1,10 +1,6 @@
import copy
from copy import deepcopy
import json
import os
import jsgf
class NLU:
def __init__(self):
self.grammars = [

View File

@ -0,0 +1,4 @@
colorama~=0.4.6
pydantic~=2.7.0
pandas~=1.5.3
numpy~=1.26.4

1
run.sh Normal file
View File

@ -0,0 +1 @@
docker run -it --rm -p 8888:8888 chatbot