muryshev commited on
Commit
7c28e1f
·
1 Parent(s): 42d5d08
Files changed (10) hide show
  1. .dockerignore +20 -0
  2. .gitignore +8 -0
  3. app.py +90 -0
  4. prompts.py +23 -0
  5. requirements.txt +5 -0
  6. search.py +16 -0
  7. static/chat-bot.js +81 -0
  8. static/drag-resize.js +81 -0
  9. static/index.html +80 -0
  10. static/style.css +156 -0
.dockerignore ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ *.db
6
+ *.sqlite
7
+ *.log
8
+ .DS_Store
9
+ .env
10
+ venv
11
+ *.bat
12
+ desktop.ini
13
+ *.git
14
+ .cache
15
+ .local
16
+ .nv
17
+ *.bash_history
18
+ *.zip
19
+ *.yaml
20
+ /devops
.gitignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ *.bat
2
+ __pycache__
3
+ .cache
4
+ .local
5
+ .nv
6
+ *.bash_history
7
+ *.zip
8
+ /Dockerfile
app.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, Response, jsonify, send_from_directory
2
+ import os
3
+ import requests
4
+ import json
5
+ from dotenv import load_dotenv
6
+ from search import search
7
+ import prompts
8
+
9
+ # Загрузка переменных окружения из файла .env
10
+ load_dotenv()
11
+
12
+ app = Flask(__name__, static_url_path='/static', static_folder='static')
13
+
14
+ initial_prompt = """
15
+ {запрос пользователя}
16
+
17
+ Источники:
18
+ {источники}
19
+ """
20
+
21
+ @app.route('/')
22
+ def index():
23
+ return app.send_static_file('index.html')
24
+
25
+ @app.route('/send_message', methods=['POST'])
26
+ def send_message():
27
+ data = request.json
28
+ user_message = data.get('message')
29
+ conversation_history = data.get('history', [])
30
+
31
+ if user_message:
32
+ # Добавляем системное сообщение, если его нет в истории
33
+ if not any(msg['role'] == 'system' for msg in conversation_history):
34
+ conversation_history.insert(0, {"role": "system", "content": prompts.SYSTEM_PROMPT})
35
+
36
+ _, sources = search(user_message)
37
+
38
+ formatted_user_message = prompts.MESSAGE_PROMPT_TEMPLATE.format(
39
+ ВОПРОС_ПОЛЬЗОВАТЕЛЯ=user_message,
40
+ ПОЛЕЗНАЯ_ИНФОРМАЦИЯ='\n\n'.join(sources)
41
+ )
42
+
43
+ # Добавляем сообщение пользователя в историю
44
+ conversation_history.append({"role": "user", "content": formatted_user_message, "display": user_message})
45
+
46
+ # Отправляем историю сообщений в API
47
+ response = fetch_deep_infra_response(conversation_history)
48
+
49
+ # Добавляем ответ бота в историю
50
+ conversation_history.append({"role": "assistant", "content": response, "display": response})
51
+
52
+ # Используем json.dumps с ensure_ascii=False
53
+ response_data = {
54
+ 'response': response,
55
+ 'history': conversation_history
56
+ }
57
+ return Response(json.dumps(response_data, ensure_ascii=False), content_type='application/json; charset=utf-8')
58
+ return jsonify({'response': 'Please provide a message.', 'history': conversation_history}), 400
59
+
60
+ def fetch_deep_infra_response(conversation_history):
61
+ api_url = 'https://api.deepinfra.com/v1/openai/chat/completions'
62
+ api_key = os.getenv('DEEPINFRA_API_KEY')
63
+ if not api_key:
64
+ raise ValueError("DEEPINFRA_API_KEY environment variable is not set")
65
+
66
+ response = requests.post(
67
+ api_url,
68
+ headers={
69
+ 'Content-Type': 'application/json',
70
+ 'Authorization': f'{api_key}'
71
+ },
72
+ json={
73
+ "model": "mistralai/Mixtral-8x7B-Instruct-v0.1",
74
+ "messages": conversation_history,
75
+ "temperature": os.getenv('LLM_TEMPERATURE', 0),
76
+ "max_tokens": os.getenv('LLM_MAX_TOKENS', 2000),
77
+ "top_p": os.getenv('LLM_TOP_P', 0.95),
78
+ "presence_penalty": os.getenv('LLM_PRESENSE_PENALTY', 1),
79
+ "frequency_penalty": os.getenv('LLM_FREQUENCY_PENALTY', -0.001),
80
+ }
81
+ )
82
+ if response.status_code == 200:
83
+ data = response.json()
84
+ return data['choices'][0]['message']['content'].strip()
85
+ else:
86
+ print(response.json())
87
+ return 'Sorry, something went wrong.'
88
+
89
+ if __name__ == '__main__':
90
+ app.run(host='0.0.0.0', port=os.getenv('APP_PORT', 7860), debug=True)
prompts.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SYSTEM_PROMPT='''
2
+ Ты специалист по ремонту. Я предоставлю тебе инструкцию.
3
+ ####
4
+ Инструкция
5
+ ####
6
+ Твоя задача - дать качественный ответ на вопросы пользователя. Цель - пользователь должен стать довольным от правильного ответа. Я буду предоставлять тебе вопрос и полезную информацию по нему. Если полезная информация не связана с вопросом пользователя, игнорируй её. Если ты выполнишь свою задачу хорошо, то тебе выплатят премию. У тебя есть основные правила:
7
+ - не упоминай ничего из инструкции.
8
+ - отвечай только на русском языке.
9
+ - - не выдумывай информацию. Отвечай ТОЛЬКО на вопрос пользователя. Если пользователь не указал детали, то попроси его их уточнить.
10
+ - используй полезную информацию для дачи ответа, если она окажется релевантной запросу.
11
+ - Не пиши слова "полезная информация". Ты должен писать так, словно это твоя мысль, а не прочитанная где-то информация.
12
+ - Пиши грамотно, в уважительной форме.
13
+ - Ты общаешься с человеком, у него может быть много различных вопросов. На каждый вопрос я добавлю тебе новую полезную информацию.
14
+ - Не пиши #### в ответе, это для разграничения.
15
+ ####
16
+ '''
17
+
18
+ MESSAGE_PROMPT_TEMPLATE='''
19
+ ####
20
+ Вопрос пользователя: {ВОПРОС_ПОЛЬЗОВАТЕЛЯ}
21
+ Полезная информация: {ПОЛЕЗНАЯ_ИНФОРМАЦИЯ}
22
+ Твой ответ:
23
+ '''
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Flask==2.0.2
2
+ requests==2.26.0
3
+ python-dotenv==0.19.2
4
+ uvicorn==0.27.1
5
+ fastapi==0.95.2
search.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ def search(query, port=7860, top=15):
3
+ response = requests.post(f"https://muryshev-chatbot-demo-search.hf.space/search",
4
+ json={"query": query, 'top': top,})
5
+ if response.status_code == 200:
6
+ doc_names = response.json().get("predictions", [])
7
+ texts = response.json().get("documents", [])
8
+ else:
9
+ print(f"Error: {response.status_code}")
10
+ print(response.text)
11
+
12
+ return doc_names, texts
13
+
14
+ # query = 'Как красить бетон?'
15
+ # names, texts = search(query)
16
+ # print(texts)
static/chat-bot.js ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let conversationHistory = [];
2
+
3
+ function sendMessage() {
4
+ const userInput = document.getElementById('user-input');
5
+ const submitButton = document.getElementById('submit-button');
6
+ const userMessage = userInput.value.trim();
7
+ if (userMessage) {
8
+ addMessage('user', userMessage, 'Вы');
9
+ userInput.value = '';
10
+
11
+ // Добавляем сообщение пользователя в историю
12
+ // conversationHistory.push({"role": "user", "content": userMessage, "display": userMessage});
13
+
14
+ // Ограничиваем историю до 20 сообщений
15
+ if (conversationHistory.length > 20) {
16
+ conversationHistory.shift();
17
+ }
18
+ submitButton.innerText = 'Отвечаем...'
19
+ submitButton.disabled = true
20
+ // Отправляем сообщение и историю на сервер
21
+ fetch('/send_message', {
22
+ method: 'POST',
23
+ headers: {
24
+ 'Content-Type': 'application/json'
25
+ },
26
+ body: JSON.stringify({ history: conversationHistory, message: userMessage })
27
+ })
28
+ .then(response => response.json())
29
+ .then(data => {
30
+ addMessage('assistant', data.response, 'Ассистент');
31
+ conversationHistory = data.history; // Обновляем историю сообщений
32
+ saveConversationHistory();
33
+ })
34
+ .catch(error => {
35
+ console.error('Error:', error);
36
+ addMessage('assistant', 'Sorry, something went wrong.', 'Ассистент');
37
+ })
38
+ .finally(e => {
39
+ submitButton.innerText = 'Отправить'
40
+ submitButton.disabled = false
41
+ })
42
+ }
43
+ }
44
+
45
+ function addMessage(sender, message, role) {
46
+ if(role == 'system') return
47
+ const chatMessages = document.getElementById('chat-messages');
48
+ const messageElement = document.createElement('div');
49
+ messageElement.classList.add('message', sender);
50
+ const label = role === 'user' ? 'Вы' : 'Ассистент'
51
+ messageElement.innerHTML = `<strong>${label}:</strong> ${message}`;
52
+ chatMessages.appendChild(messageElement);
53
+ chatMessages.scrollTop = chatMessages.scrollHeight;
54
+ }
55
+
56
+ function saveConversationHistory() {
57
+ localStorage.setItem('conversationHistory', JSON.stringify(conversationHistory));
58
+ }
59
+
60
+ function loadConversationHistory() {
61
+ const savedHistory = localStorage.getItem('conversationHistory');
62
+ if (savedHistory) {
63
+ conversationHistory = JSON.parse(savedHistory);
64
+ conversationHistory.forEach(msg => addMessage(msg.role, msg.display, msg.role));
65
+ }
66
+ }
67
+
68
+ document.addEventListener('DOMContentLoaded', () => {
69
+ loadConversationHistory();
70
+
71
+ document.getElementById('clear-history').addEventListener('click', () => {
72
+ clearConversationHistory();
73
+ });
74
+ });
75
+
76
+ function clearConversationHistory() {
77
+ conversationHistory = [];
78
+ const chatMessages = document.getElementById('chat-messages');
79
+ chatMessages.innerHTML = '';
80
+ localStorage.removeItem('conversationHistory');
81
+ }
static/drag-resize.js ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ const chatBotContainer = document.getElementById('chat-bot-container');
3
+ let isDragging = false;
4
+ let offsetX, offsetY;
5
+ let saveTimeout;
6
+
7
+ // Загрузка позиции и размера из localStorage
8
+ const savedPosition = localStorage.getItem('chatBotPosition');
9
+ const savedSize = localStorage.getItem('chatBotSize');
10
+
11
+ if (savedPosition) {
12
+ const { left, top } = JSON.parse(savedPosition);
13
+ chatBotContainer.style.left = `${left}px`;
14
+ chatBotContainer.style.top = `${top}px`;
15
+ }
16
+
17
+ if (savedSize) {
18
+ const { width, height } = JSON.parse(savedSize);
19
+ chatBotContainer.style.width = `${width}px`;
20
+ chatBotContainer.style.height = `${height}px`;
21
+ }
22
+
23
+ // Добавляем обработчики для перетаскивания
24
+ chatBotContainer.querySelector('.chat-header').addEventListener('mousedown', (e) => {
25
+ isDragging = true;
26
+ offsetX = e.clientX - chatBotContainer.offsetLeft;
27
+ offsetY = e.clientY - chatBotContainer.offsetTop;
28
+ });
29
+
30
+ document.addEventListener('mousemove', (e) => {
31
+ if (isDragging) {
32
+ chatBotContainer.style.left = `${e.clientX - offsetX}px`;
33
+ chatBotContainer.style.top = `${e.clientY - offsetY}px`;
34
+ }
35
+ });
36
+
37
+ document.addEventListener('mouseup', () => {
38
+ isDragging = false;
39
+ scheduleSave();
40
+ });
41
+
42
+ // // Добавляем обработчики для изменения размера
43
+ // chatBotContainer.addEventListener('mousedown', (e) => {
44
+ // if (e.target.classList.contains('resizable')) {
45
+ // isDragging = true;
46
+ // offsetX = e.clientX - chatBotContainer.offsetWidth;
47
+ // offsetY = e.clientY - chatBotContainer.offsetHeight;
48
+ // }
49
+ // });
50
+
51
+ document.addEventListener('mousemove', (e) => {
52
+ if (isDragging && e.target.classList.contains('resizable')) {
53
+ chatBotContainer.style.width = `${e.clientX - offsetX}px`;
54
+ chatBotContainer.style.height = `${e.clientY - offsetY}px`;
55
+ }
56
+ });
57
+
58
+ document.addEventListener('mouseup', () => {
59
+ isDragging = false;
60
+ scheduleSave();
61
+ });
62
+
63
+ function scheduleSave() {
64
+ clearTimeout(saveTimeout);
65
+ saveTimeout = setTimeout(savePositionAndSize, 200); // Задержка в 200 миллисекунд
66
+ }
67
+
68
+ function savePositionAndSize() {
69
+ const position = {
70
+ left: chatBotContainer.offsetLeft,
71
+ top: chatBotContainer.offsetTop
72
+ };
73
+ const size = {
74
+ width: chatBotContainer.offsetWidth,
75
+ height: chatBotContainer.offsetHeight
76
+ };
77
+
78
+ localStorage.setItem('chatBotPosition', JSON.stringify(position));
79
+ localStorage.setItem('chatBotSize', JSON.stringify(size));
80
+ }
81
+ });
static/index.html ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Строительная Компания</title>
8
+ <link rel="stylesheet" href="/static/style.css">
9
+ </head>
10
+
11
+ <body>
12
+ <header>
13
+ <h1>Добро пожаловать на сайт нашей строительной компании</h1>
14
+ <nav>
15
+ <ul>
16
+ <li><a href="#about">О нас</a></li>
17
+ <li><a href="#services">Услуги</a></li>
18
+ <li><a href="#projects">Проекты</a></li>
19
+ <li><a href="#contact">Контакты</a></li>
20
+ </ul>
21
+ </nav>
22
+ </header>
23
+
24
+ <section id="about">
25
+ <h2>О нас</h2>
26
+ <p>Наша компания специализируется на строительстве и ремонте зданий. Мы предлагаем высококачественные услуги,
27
+ которые удовлетворят потребности любого клиента.</p>
28
+ </section>
29
+
30
+ <section id="services">
31
+ <h2>Услуги</h2>
32
+ <ul>
33
+ <li>Строительство домов</li>
34
+ <li>Ремонт квартир</li>
35
+ <li>Ландшафтный дизайн</li>
36
+ <li>Электромонтажные работы</li>
37
+ </ul>
38
+ </section>
39
+
40
+ <section id="projects">
41
+ <h2>Проекты</h2>
42
+ <p>Мы гордимся своими проектами. Вот некоторые из них:</p>
43
+ <div class="project">
44
+ <img src="project1.jpg" alt="Проект 1">
45
+ <p>Описание проекта 1</p>
46
+ </div>
47
+ <div class="project">
48
+ <img src="project2.jpg" alt="Проект 2">
49
+ <p>Описание проекта 2</p>
50
+ </div>
51
+ </section>
52
+
53
+ <section id="contact">
54
+ <h2>Контакты</h2>
55
+ <p>Свяжитесь с нами по телефону: +7 (123) 456-78-90</p>
56
+ <p>Email: [email protected]</p>
57
+ </section>
58
+
59
+ <footer>
60
+ <p>&copy; 2023 Строительная Компания. Все права защищены.</p>
61
+ </footer>
62
+
63
+ <!-- Чат-бот -->
64
+ <div id="chat-bot-container" class="draggable resizable">
65
+ <div class="chat-header">ИИ консультант</div>
66
+ <div class="chat-messages" id="chat-messages"></div>
67
+ <div class="chat-input">
68
+ <form onsubmit="sendMessage(); return false;" action="#">
69
+ <input type="text" id="user-input" placeholder="Введите запрос...">
70
+ <button type="submit" id="submit-button">Отправить</button>
71
+ </form>
72
+ </div>
73
+ <button id="clear-history" onclick="clearConversationHistory()" style="display: block; margin-top: 10px">Удалить историю</button>
74
+ </div>
75
+
76
+ <script src="/static/chat-bot.js"></script>
77
+ <script src="/static/drag-resize.js"></script>
78
+ </body>
79
+
80
+ </html>
static/style.css ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ font-family: Arial, sans-serif;
3
+ margin: 0;
4
+ padding: 0;
5
+ background-color: #f0f0f0;
6
+ }
7
+
8
+ header {
9
+ background: linear-gradient(90deg, #64b5f6, #1e88e5);
10
+ color: #fff;
11
+ padding: 20px;
12
+ text-align: center;
13
+ }
14
+
15
+ nav ul {
16
+ list-style: none;
17
+ padding: 0;
18
+ }
19
+
20
+ nav ul li {
21
+ display: inline;
22
+ margin: 0 10px;
23
+ }
24
+
25
+ nav ul li a {
26
+ color: #fff;
27
+ text-decoration: none;
28
+ }
29
+
30
+ section {
31
+ padding: 20px;
32
+ margin: 20px;
33
+ background-color: #fff;
34
+ border-radius: 5px;
35
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
36
+ }
37
+
38
+ .project img {
39
+ max-width: 100%;
40
+ height: auto;
41
+ }
42
+
43
+ footer {
44
+ background-color: #64b5f6;
45
+ color: #fff;
46
+ text-align: center;
47
+ padding: 10px;
48
+ position: fixed;
49
+ bottom: 0;
50
+ width: 100%;
51
+ }
52
+
53
+ #chat-bot-container {
54
+ position: fixed;
55
+ bottom: 20px;
56
+ right: 20px;
57
+ width: 300px;
58
+ height: 400px;
59
+ background-color: #fff;
60
+ border-radius: 10px;
61
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
62
+ z-index: 1000;
63
+ display: flex;
64
+ flex-direction: column;
65
+ overflow: hidden;
66
+ }
67
+
68
+ .chat-header {
69
+ background: linear-gradient(90deg, #1e88e5, #6455f6);
70
+ color: #fff;
71
+ padding: 10px;
72
+ text-align: center;
73
+ font-size: 18px;
74
+ cursor: move;
75
+ }
76
+
77
+ .chat-messages {
78
+ flex: 1;
79
+ padding: 10px;
80
+ overflow-y: auto;
81
+ border-bottom: 1px solid #ddd;
82
+ }
83
+
84
+ .chat-input {
85
+ display: flex;
86
+ padding: 10px;
87
+ background-color: #f9f9f9;
88
+ }
89
+
90
+ .chat-input form {
91
+ display: flex;
92
+ width: 100%;
93
+ }
94
+
95
+ .chat-input input {
96
+ flex: 1;
97
+ padding: 10px;
98
+ border: none;
99
+ border-radius: 5px;
100
+ margin-right: 10px;
101
+ }
102
+
103
+ .chat-input button {
104
+ padding: 10px 20px;
105
+ border: none;
106
+ background-color: #64b5f6;
107
+ color: #fff;
108
+ border-radius: 5px;
109
+ cursor: pointer;
110
+ }
111
+
112
+ .chat-input button:hover {
113
+ background-color: #1e88e5;
114
+ }
115
+
116
+ .message {
117
+ margin-bottom: 10px;
118
+ padding: 10px;
119
+ border-radius: 5px;
120
+ }
121
+
122
+ .message.user {
123
+ background-color: #b5d8f5;
124
+ align-self: flex-end;
125
+ }
126
+
127
+ .message.assistant {
128
+ background-color: #e1e1ff;
129
+ align-self: flex-start;
130
+ }
131
+
132
+ .resizable {
133
+ resize: both;
134
+ overflow: auto;
135
+ cursor: se-resize;
136
+ }
137
+
138
+ .draggable {
139
+ cursor: move;
140
+ }
141
+
142
+ #clear-history {
143
+ margin-top: 10px;
144
+ padding: 5px 10px;
145
+ border: none;
146
+ background-color: transparent;
147
+ color: #000;
148
+ font-size: 12px;
149
+ text-decoration: underline;
150
+ cursor: pointer;
151
+ }
152
+
153
+ #clear-history:hover {
154
+ text-decoration: none;
155
+ color: #555;
156
+ }