feat: add chatbot config and exercise
This commit is contained in:
parent
f039d6462b
commit
5996ff712a
13
Dockerfile
Normal file
13
Dockerfile
Normal 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"]
|
41
README.md
41
README.md
@ -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 |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"data_path": "./data/intents.json"
|
|
||||||
}
|
|
4
chatbot/config/config.json
Normal file
4
chatbot/config/config.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"data_path": "./data/intents.json",
|
||||||
|
"responses_path": "./data/response.json"
|
||||||
|
}
|
@ -1,26 +1,22 @@
|
|||||||
from modules.nlp import NaturalLanguageProcessor, Config
|
from pathlib import Path
|
||||||
from modules.state_monitor import DialogueStateMonitor
|
from modules.nlp import NaturalLanguageProcessor
|
||||||
from modules.strategy import DialogueStrategy
|
from modules.generator import ResponseGenerator
|
||||||
from modules.generator import NaturalLanguageGenerator
|
from modules.config import Config
|
||||||
import colorama
|
import colorama
|
||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
|
|
||||||
colorama.init(autoreset=True)
|
colorama.init(autoreset=True)
|
||||||
|
|
||||||
|
|
||||||
def chatbot_response(input_text: str, nlp: NaturalLanguageProcessor) -> str:
|
def main():
|
||||||
dialogue_monitor = DialogueStateMonitor()
|
base_path = Path(__file__).resolve().parent
|
||||||
analysis = nlp.analyze(input_text)
|
config_path = base_path / 'config' / 'config.json'
|
||||||
dialogue_monitor.update_state(analysis['intent'])
|
config = Config.load_config(config_path)
|
||||||
response = DialogueStrategy.decide_response(dialogue_monitor.state)
|
|
||||||
final_response = NaturalLanguageGenerator.generate(response)
|
|
||||||
|
|
||||||
return final_response
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
config = Config()
|
|
||||||
nlp = NaturalLanguageProcessor(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")
|
print(Fore.YELLOW + "Wpisz 'quit' aby zakończyć program.\n")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@ -29,4 +25,10 @@ if __name__ == "__main__":
|
|||||||
print(Fore.RED + "Zamykanie chatbota...")
|
print(Fore.RED + "Zamykanie chatbota...")
|
||||||
break
|
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()
|
||||||
|
0
chatbot/modules/__init__.py
Normal file
0
chatbot/modules/__init__.py
Normal file
24
chatbot/modules/config.py
Normal file
24
chatbot/modules/config.py
Normal 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)
|
@ -1,4 +1,13 @@
|
|||||||
class NaturalLanguageGenerator:
|
import json
|
||||||
@staticmethod
|
from typing import Dict
|
||||||
def generate(response: str) -> str:
|
from .config import Config
|
||||||
return response
|
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ć?"]))
|
||||||
|
@ -1,38 +1,20 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
from typing import Dict, List, TypedDict
|
||||||
from typing import Any, Dict, Literal
|
from .config import Config
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Intents(TypedDict):
|
||||||
def __init__(self) -> None:
|
name_query: List[str]
|
||||||
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 NaturalLanguageProcessor:
|
class NaturalLanguageProcessor:
|
||||||
def __init__(self, config: Config) -> None:
|
def __init__(self, config: Config):
|
||||||
self.config = config
|
with config.data_path.open('r', encoding='utf-8') as file:
|
||||||
data_path = self.config.get_data_path()
|
self.intents: Intents = json.load(file)
|
||||||
with open(data_path, 'r', encoding='utf-8') as file:
|
|
||||||
self.intents: Dict[str, Any] = 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()
|
lower_text = input_text.lower()
|
||||||
for phrase in self.intents.get('name_query', []):
|
for intent, phrases in self.intents.items():
|
||||||
if phrase in lower_text:
|
if any(phrase in lower_text for phrase in phrases):
|
||||||
return {"intent": "ask_name"}
|
return intent
|
||||||
return {"intent": "unknown"}
|
return "unknown"
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
class DialogueStrategy:
|
class DialogueStrategy:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decide_response(state: dict) -> str:
|
def decide_response(state: dict, responses) -> str:
|
||||||
if state['last_intent'] == 'ask_name':
|
intent_responses = responses.get(state['last_intent'], ["Przepraszam, nie rozumiem. Możesz to powtórzyć?"])
|
||||||
return "Witaj, nazywam się Dia."
|
return random.choice(intent_responses)
|
||||||
else:
|
|
||||||
return "Przepraszam, nie rozumiem. Możesz to powtórzyć?"
|
|
||||||
|
@ -2,7 +2,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from chatbot.modules.nlu import NLU
|
from modules.nlu import NLU
|
||||||
|
|
||||||
rows = 0
|
rows = 0
|
||||||
hits = 0
|
hits = 0
|
||||||
|
0
modules/__init__.py
Normal file
0
modules/__init__.py
Normal file
@ -1,10 +1,6 @@
|
|||||||
import copy
|
|
||||||
from copy import deepcopy
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import jsgf
|
import jsgf
|
||||||
|
|
||||||
|
|
||||||
class NLU:
|
class NLU:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.grammars = [
|
self.grammars = [
|
@ -0,0 +1,4 @@
|
|||||||
|
colorama~=0.4.6
|
||||||
|
pydantic~=2.7.0
|
||||||
|
pandas~=1.5.3
|
||||||
|
numpy~=1.26.4
|
Loading…
Reference in New Issue
Block a user