smart-bot-factory 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of smart-bot-factory might be problematic. Click here for more details.
- smart_bot_factory/__init__.py +51 -0
- smart_bot_factory/admin/__init__.py +16 -0
- smart_bot_factory/admin/admin_logic.py +430 -0
- smart_bot_factory/admin/admin_manager.py +141 -0
- smart_bot_factory/admin/admin_migration.sql +136 -0
- smart_bot_factory/admin/admin_tester.py +151 -0
- smart_bot_factory/admin/timeout_checker.py +499 -0
- smart_bot_factory/analytics/__init__.py +7 -0
- smart_bot_factory/analytics/analytics_manager.py +355 -0
- smart_bot_factory/cli.py +642 -0
- smart_bot_factory/config.py +235 -0
- smart_bot_factory/configs/growthmed-helper/env_example.txt +1 -0
- smart_bot_factory/configs/growthmed-helper/prompts/1sales_context.txt +9 -0
- smart_bot_factory/configs/growthmed-helper/prompts/2product_info.txt +582 -0
- smart_bot_factory/configs/growthmed-helper/prompts/3objection_handling.txt +66 -0
- smart_bot_factory/configs/growthmed-helper/prompts/final_instructions.txt +232 -0
- smart_bot_factory/configs/growthmed-helper/prompts/help_message.txt +28 -0
- smart_bot_factory/configs/growthmed-helper/prompts/welcome_message.txt +7 -0
- smart_bot_factory/configs/growthmed-helper/welcome_file/welcome_file_msg.txt +16 -0
- smart_bot_factory/configs/growthmed-helper/welcome_file//342/225/250/320/267/342/225/250/342/225/241/342/225/250/342/225/221 /342/225/250/342/225/227/342/225/250/342/225/225/342/225/244/320/221/342/225/244/320/222 /342/225/250/342/224/220/342/225/250/342/225/233 152/342/225/250/320/264/342/225/250/320/247 /342/225/250/342/225/225 323/342/225/250/320/264/342/225/250/320/247 /342/225/250/342/224/244/342/225/250/342/225/227/342/225/244/320/237 /342/225/250/342/225/235/342/225/250/342/225/241/342/225/250/342/224/244/342/225/250/342/225/225/342/225/244/320/226/342/225/250/342/225/225/342/225/250/342/225/234/342/225/244/320/233.pdf +0 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt +16 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt +582 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt +66 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +212 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/help_message.txt +28 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/welcome_message.txt +8 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064229.txt +818 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064335.txt +32 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064638.txt +35 -0
- smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +66 -0
- smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml +108 -0
- smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml +46 -0
- smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt +16 -0
- smart_bot_factory/configs/growthmed-october-24/welcome_file//342/225/250/320/267/342/225/250/342/225/241/342/225/250/342/225/221 /342/225/250/342/225/227/342/225/250/342/225/225/342/225/244/320/221/342/225/244/320/222 /342/225/250/342/224/220/342/225/250/342/225/233 152/342/225/250/320/264/342/225/250/320/247 /342/225/250/342/225/225 323/342/225/250/320/264/342/225/250/320/247 /342/225/250/342/224/244/342/225/250/342/225/227/342/225/244/320/237 /342/225/250/342/225/235/342/225/250/342/225/241/342/225/250/342/224/244/342/225/250/342/225/225/342/225/244/320/226/342/225/250/342/225/225/342/225/250/342/225/234/342/225/244/320/233.pdf +0 -0
- smart_bot_factory/core/__init__.py +22 -0
- smart_bot_factory/core/bot_utils.py +693 -0
- smart_bot_factory/core/conversation_manager.py +536 -0
- smart_bot_factory/core/decorators.py +229 -0
- smart_bot_factory/core/message_sender.py +249 -0
- smart_bot_factory/core/states.py +14 -0
- smart_bot_factory/creation/__init__.py +8 -0
- smart_bot_factory/creation/bot_builder.py +329 -0
- smart_bot_factory/creation/bot_testing.py +986 -0
- smart_bot_factory/database/database_structure.sql +57 -0
- smart_bot_factory/database/schema.sql +1094 -0
- smart_bot_factory/handlers/handlers.py +583 -0
- smart_bot_factory/integrations/__init__.py +9 -0
- smart_bot_factory/integrations/openai_client.py +435 -0
- smart_bot_factory/integrations/supabase_client.py +592 -0
- smart_bot_factory/setup_checker.py +476 -0
- smart_bot_factory/utils/__init__.py +9 -0
- smart_bot_factory/utils/debug_routing.py +103 -0
- smart_bot_factory/utils/prompt_loader.py +427 -0
- smart_bot_factory/uv.lock +2004 -0
- smart_bot_factory-0.1.3.dist-info/METADATA +126 -0
- smart_bot_factory-0.1.3.dist-info/RECORD +59 -0
- smart_bot_factory-0.1.3.dist-info/licenses/LICENSE +24 -0
- smart_bot_factory-0.1.2.dist-info/METADATA +0 -31
- smart_bot_factory-0.1.2.dist-info/RECORD +0 -4
- {smart_bot_factory-0.1.2.dist-info → smart_bot_factory-0.1.3.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.1.2.dist-info → smart_bot_factory-0.1.3.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# Обновленный prompt_loader.py с поддержкой финальных инструкций
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import aiofiles
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import List, Dict
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
class PromptLoader:
|
|
11
|
+
"""Класс для загрузки промптов из локального каталога"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, prompts_dir: str):
|
|
14
|
+
self.prompts_dir = Path(prompts_dir)
|
|
15
|
+
self.welcome_file = self.prompts_dir / 'welcome_message.txt'
|
|
16
|
+
self.help_file = self.prompts_dir / 'help_message.txt'
|
|
17
|
+
self.final_instructions_file = self.prompts_dir / 'final_instructions.txt'
|
|
18
|
+
|
|
19
|
+
# Автоматически находим все .txt файлы промптов (кроме специальных)
|
|
20
|
+
all_txt_files = list(self.prompts_dir.glob('*.txt'))
|
|
21
|
+
special_files = {'welcome_message.txt', 'help_message.txt', 'final_instructions.txt'}
|
|
22
|
+
self.prompt_files = [f.name for f in all_txt_files if f.name not in special_files]
|
|
23
|
+
|
|
24
|
+
logger.info(f"Инициализирован загрузчик промптов: {self.prompts_dir}")
|
|
25
|
+
logger.info(f"Найдено файлов промптов: {len(self.prompt_files)}")
|
|
26
|
+
logger.info(f"Файлы промптов: {self.prompt_files}")
|
|
27
|
+
|
|
28
|
+
async def load_system_prompt(self) -> str:
|
|
29
|
+
"""
|
|
30
|
+
Загружает и объединяет все файлы промптов в один системный промпт
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Объединенный системный промпт с инструкциями по JSON
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
prompt_parts = []
|
|
37
|
+
|
|
38
|
+
for filename in self.prompt_files:
|
|
39
|
+
logger.debug(f"Загружаем промпт из {filename}")
|
|
40
|
+
content = await self._load_file(filename)
|
|
41
|
+
|
|
42
|
+
if content:
|
|
43
|
+
# Добавляем заголовок секции
|
|
44
|
+
section_name = self._get_section_name(filename)
|
|
45
|
+
prompt_parts.append(f"\n### {section_name} ###\n")
|
|
46
|
+
prompt_parts.append(content.strip())
|
|
47
|
+
prompt_parts.append("\n")
|
|
48
|
+
else:
|
|
49
|
+
logger.warning(f"Файл {filename} пуст")
|
|
50
|
+
|
|
51
|
+
if not prompt_parts:
|
|
52
|
+
error_msg = "Не удалось загрузить ни одного промпт файла"
|
|
53
|
+
logger.error(error_msg)
|
|
54
|
+
raise ValueError(error_msg)
|
|
55
|
+
|
|
56
|
+
# Добавляем инструкции по JSON метаданным
|
|
57
|
+
json_instructions = self._get_json_instructions()
|
|
58
|
+
prompt_parts.append("\n")
|
|
59
|
+
prompt_parts.append(json_instructions)
|
|
60
|
+
|
|
61
|
+
# Объединяем все части
|
|
62
|
+
full_prompt = "".join(prompt_parts).strip()
|
|
63
|
+
|
|
64
|
+
logger.info(f"Системный промпт загружен успешно ({len(full_prompt)} символов)")
|
|
65
|
+
return full_prompt
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.error(f"Ошибка при загрузке системного промпта: {e}")
|
|
69
|
+
raise
|
|
70
|
+
|
|
71
|
+
async def load_final_instructions(self) -> str:
|
|
72
|
+
"""
|
|
73
|
+
Загружает финальные инструкции из final_instructions.txt
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Финальные инструкции или пустая строка если файла нет
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
logger.debug(f"Загружаем финальные инструкции из {self.final_instructions_file.name}")
|
|
80
|
+
|
|
81
|
+
if not self.final_instructions_file.exists():
|
|
82
|
+
logger.debug(f"Файл {self.final_instructions_file.name} не найден - пропускаем")
|
|
83
|
+
return ""
|
|
84
|
+
|
|
85
|
+
async with aiofiles.open(self.final_instructions_file, 'r', encoding='utf-8') as f:
|
|
86
|
+
content = await f.read()
|
|
87
|
+
|
|
88
|
+
if not content.strip():
|
|
89
|
+
logger.debug(f"Файл {self.final_instructions_file.name} пуст - пропускаем")
|
|
90
|
+
return ""
|
|
91
|
+
|
|
92
|
+
logger.info(f"Финальные инструкции загружены ({len(content)} символов)")
|
|
93
|
+
return content.strip()
|
|
94
|
+
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.error(f"Ошибка при загрузке финальных инструкций: {e}")
|
|
97
|
+
# Не прерываем работу - финальные инструкции опциональны
|
|
98
|
+
return ""
|
|
99
|
+
|
|
100
|
+
def _get_json_instructions(self) -> str:
|
|
101
|
+
"""Возвращает инструкции по JSON метаданным для ИИ"""
|
|
102
|
+
return """
|
|
103
|
+
### КРИТИЧЕСКИ ВАЖНЫЕ ИНСТРУКЦИИ ПО JSON МЕТАДАННЫМ ###
|
|
104
|
+
|
|
105
|
+
В КОНЦЕ КАЖДОГО своего ответа ОБЯЗАТЕЛЬНО добавляй служебную информацию в JSON формате:
|
|
106
|
+
|
|
107
|
+
{
|
|
108
|
+
"этап": "introduction|consult|offer|contacts",
|
|
109
|
+
"качество": 1-10,
|
|
110
|
+
"события": [
|
|
111
|
+
{
|
|
112
|
+
"тип": "телефон|консультация|покупка|отказ",
|
|
113
|
+
"инфо": "детали события"
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"файлы": ["file1.pdf", "file2.mp4"],
|
|
117
|
+
"каталоги": ["каталог1", "каталог2"]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
ОПИСАНИЕ ЭТАПОВ:
|
|
121
|
+
- introduction: знакомство, сбор базовой информации о клиенте
|
|
122
|
+
- consult: консультирование, ответы на вопросы о конференции
|
|
123
|
+
- offer: предложение тарифов, обработка возражений
|
|
124
|
+
- contacts: получение контактов, завершение сделки
|
|
125
|
+
|
|
126
|
+
СИСТЕМА ОЦЕНКИ КАЧЕСТВА (1-10):
|
|
127
|
+
1-3: низкий интерес, много возражений, скептически настроен
|
|
128
|
+
4-6: средний интерес, есть вопросы, обдумывает
|
|
129
|
+
7-8: высокий интерес, готов к покупке, активно интересуется
|
|
130
|
+
9-10: горячий лид, предоставил контакты или готов к действию
|
|
131
|
+
|
|
132
|
+
СОБЫТИЯ - добавляй ТОЛЬКО когда происходит что-то из этого:
|
|
133
|
+
- "телефон": пользователь предоставил номер телефона
|
|
134
|
+
- "консультация": пользователь просит живую консультацию по телефону
|
|
135
|
+
- "покупка": пользователь готов купить/записаться на конференцию
|
|
136
|
+
- "отказ": пользователь явно отказывается участвовать
|
|
137
|
+
|
|
138
|
+
ВАЖНО:
|
|
139
|
+
- Добавляй файлы и катологи только в том случае, если они относятся к этому этапу. (Прописаны в самом этапе) Если не относятся - отсылай пустой массив []
|
|
140
|
+
|
|
141
|
+
ПРИМЕРЫ ПРАВИЛЬНОГО ИСПОЛЬЗОВАНИЯ:
|
|
142
|
+
|
|
143
|
+
Пример 1 - обычный диалог:
|
|
144
|
+
"Расскажу подробнее о конференции GrowthMED. Она пройдет 24-25 октября..."
|
|
145
|
+
|
|
146
|
+
{
|
|
147
|
+
"этап": "consult",
|
|
148
|
+
"качество": 6,
|
|
149
|
+
"события": [],
|
|
150
|
+
"файлы": [],
|
|
151
|
+
"каталоги": []
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
Пример 2 - получен телефон:
|
|
155
|
+
"Отлично! Записал ваш номер. Мы перезвоним в течение 10 минут!"
|
|
156
|
+
|
|
157
|
+
{
|
|
158
|
+
"этап": "contacts",
|
|
159
|
+
"качество": 9,
|
|
160
|
+
"события": [
|
|
161
|
+
{
|
|
162
|
+
"тип": "телефон",
|
|
163
|
+
"инфо": "Иван Петров +79219603144"
|
|
164
|
+
}
|
|
165
|
+
],
|
|
166
|
+
"файлы": [],
|
|
167
|
+
"каталоги": []
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
Пример 3 - отправка презентации:
|
|
171
|
+
"Отправляю вам презентацию о нашей компании и прайс-лист с актуальными ценами."
|
|
172
|
+
|
|
173
|
+
{
|
|
174
|
+
"этап": "offer",
|
|
175
|
+
"качество": 7,
|
|
176
|
+
"события": [
|
|
177
|
+
{
|
|
178
|
+
"тип": "консультация",
|
|
179
|
+
"инфо": "Запросил материалы"
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
"файлы": ["презентация.pdf", "прайс.pdf"],
|
|
183
|
+
"каталоги": []
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
Пример 4 - отправка файлов из каталога:
|
|
187
|
+
"В каталоге 'примеры_работ' вы можете посмотреть наши последние проекты."
|
|
188
|
+
|
|
189
|
+
{
|
|
190
|
+
"этап": "presentation",
|
|
191
|
+
"качество": 8,
|
|
192
|
+
"события": [],
|
|
193
|
+
"файлы": [],
|
|
194
|
+
"каталоги": ["примеры_работ"]
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
Пример 5 - комбинированная отправка:
|
|
198
|
+
"Отправляю вам коммерческое предложение и примеры похожих проектов из нашего портфолио."
|
|
199
|
+
|
|
200
|
+
{
|
|
201
|
+
"этап": "offer",
|
|
202
|
+
"качество": 9,
|
|
203
|
+
"события": [
|
|
204
|
+
{
|
|
205
|
+
"тип": "предложение",
|
|
206
|
+
"инфо": "Отправлено КП"
|
|
207
|
+
}
|
|
208
|
+
],
|
|
209
|
+
"файлы": ["коммерческое_предложение.pdf"],
|
|
210
|
+
"каталоги": ["портфолио_2023"]
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
ТРЕБОВАНИЯ К JSON:
|
|
214
|
+
- JSON должен быть валидным и находиться в самом конце ответа
|
|
215
|
+
- Всегда используй кавычки для строк
|
|
216
|
+
- Массив "события" может быть пустым []
|
|
217
|
+
- Если событий нет - не добавляй их в массив
|
|
218
|
+
- Качество должно быть числом от 1 до 10
|
|
219
|
+
|
|
220
|
+
ПОМНИ: Этот JSON критически важен для работы системы администрирования и аналитики!
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
async def load_welcome_message(self) -> str:
|
|
224
|
+
"""
|
|
225
|
+
Загружает приветственное сообщение из welcome_message.txt
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Текст приветственного сообщения
|
|
229
|
+
"""
|
|
230
|
+
try:
|
|
231
|
+
logger.debug(f"Загружаем приветственное сообщение из {self.welcome_file.name}")
|
|
232
|
+
|
|
233
|
+
if not self.welcome_file.exists():
|
|
234
|
+
error_msg = f"Файл приветствия не найден: {self.welcome_file}"
|
|
235
|
+
logger.error(error_msg)
|
|
236
|
+
raise FileNotFoundError(error_msg)
|
|
237
|
+
|
|
238
|
+
async with aiofiles.open(self.welcome_file, 'r', encoding='utf-8') as f:
|
|
239
|
+
content = await f.read()
|
|
240
|
+
|
|
241
|
+
if not content.strip():
|
|
242
|
+
error_msg = f"Файл приветствия пуст: {self.welcome_file}"
|
|
243
|
+
logger.error(error_msg)
|
|
244
|
+
raise ValueError(error_msg)
|
|
245
|
+
|
|
246
|
+
logger.info(f"Приветственное сообщение загружено ({len(content)} символов)")
|
|
247
|
+
return content.strip()
|
|
248
|
+
|
|
249
|
+
except Exception as e:
|
|
250
|
+
logger.error(f"Ошибка при загрузке приветственного сообщения: {e}")
|
|
251
|
+
raise
|
|
252
|
+
|
|
253
|
+
async def load_help_message(self) -> str:
|
|
254
|
+
"""
|
|
255
|
+
Загружает справочное сообщение из help_message.txt
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Текст справочного сообщения
|
|
259
|
+
"""
|
|
260
|
+
try:
|
|
261
|
+
logger.debug(f"Загружаем справочное сообщение из {self.help_file.name}")
|
|
262
|
+
|
|
263
|
+
if self.help_file.exists():
|
|
264
|
+
async with aiofiles.open(self.help_file, 'r', encoding='utf-8') as f:
|
|
265
|
+
content = await f.read()
|
|
266
|
+
|
|
267
|
+
if content.strip():
|
|
268
|
+
logger.info(f"Справочное сообщение загружено ({len(content)} символов)")
|
|
269
|
+
return content.strip()
|
|
270
|
+
|
|
271
|
+
# Fallback если файл не найден или пуст
|
|
272
|
+
logger.warning("Файл help_message.txt не найден или пуст, используем дефолтную справку")
|
|
273
|
+
return "🤖 **Ваш помощник готов к работе!**\n\n**Команды:**\n/start - Начать диалог\n/help - Показать справку\n/status - Проверить статус"
|
|
274
|
+
|
|
275
|
+
except Exception as e:
|
|
276
|
+
logger.error(f"Ошибка при загрузке справочного сообщения: {e}")
|
|
277
|
+
# Возвращаем простую справку в случае ошибки
|
|
278
|
+
return "🤖 Ваш помощник готов к работе! Напишите /start для начала диалога."
|
|
279
|
+
|
|
280
|
+
async def _load_file(self, filename: str) -> str:
|
|
281
|
+
"""Загружает содержимое файла из каталога промптов"""
|
|
282
|
+
file_path = self.prompts_dir / filename
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
if not file_path.exists():
|
|
286
|
+
error_msg = f"Файл промпта не найден: {file_path}"
|
|
287
|
+
logger.error(error_msg)
|
|
288
|
+
raise FileNotFoundError(error_msg)
|
|
289
|
+
|
|
290
|
+
async with aiofiles.open(file_path, 'r', encoding='utf-8') as f:
|
|
291
|
+
content = await f.read()
|
|
292
|
+
|
|
293
|
+
if not content.strip():
|
|
294
|
+
logger.warning(f"Файл {filename} пуст")
|
|
295
|
+
return ""
|
|
296
|
+
|
|
297
|
+
logger.debug(f"Загружен файл {filename} ({len(content)} символов)")
|
|
298
|
+
return content
|
|
299
|
+
|
|
300
|
+
except Exception as e:
|
|
301
|
+
logger.error(f"Ошибка чтения файла {file_path}: {e}")
|
|
302
|
+
raise
|
|
303
|
+
|
|
304
|
+
def _get_section_name(self, filename: str) -> str:
|
|
305
|
+
"""Получает название секции по имени файла"""
|
|
306
|
+
name_mapping = {
|
|
307
|
+
'system_prompt.txt': 'СИСТЕМНЫЙ ПРОМПТ',
|
|
308
|
+
'sales_context.txt': 'КОНТЕКСТ ПРОДАЖ',
|
|
309
|
+
'product_info.txt': 'ИНФОРМАЦИЯ О ПРОДУКТЕ',
|
|
310
|
+
'objection_handling.txt': 'ОБРАБОТКА ВОЗРАЖЕНИЙ',
|
|
311
|
+
'1sales_context.txt': 'КОНТЕКСТ ПРОДАЖ',
|
|
312
|
+
'2product_info.txt': 'ИНФОРМАЦИЯ О ПРОДУКТЕ',
|
|
313
|
+
'3objection_handling.txt': 'ОБРАБОТКА ВОЗРАЖЕНИЙ',
|
|
314
|
+
'final_instructions.txt': 'ФИНАЛЬНЫЕ ИНСТРУКЦИИ' # 🆕
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return name_mapping.get(filename, filename.replace('.txt', '').upper())
|
|
318
|
+
|
|
319
|
+
async def reload_prompts(self) -> str:
|
|
320
|
+
"""Перезагружает промпты (для обновления без перезапуска бота)"""
|
|
321
|
+
logger.info("Перезагрузка промптов...")
|
|
322
|
+
return await self.load_system_prompt()
|
|
323
|
+
|
|
324
|
+
async def validate_prompts(self) -> Dict[str, bool]:
|
|
325
|
+
"""Проверяет доступность всех файлов промптов и приветственного сообщения"""
|
|
326
|
+
results = {}
|
|
327
|
+
|
|
328
|
+
# Проверяем файлы промптов
|
|
329
|
+
for filename in self.prompt_files:
|
|
330
|
+
file_path = self.prompts_dir / filename
|
|
331
|
+
try:
|
|
332
|
+
if file_path.exists():
|
|
333
|
+
async with aiofiles.open(file_path, 'r', encoding='utf-8') as f:
|
|
334
|
+
content = await f.read()
|
|
335
|
+
results[filename] = bool(content.strip() and len(content.strip()) > 10)
|
|
336
|
+
else:
|
|
337
|
+
results[filename] = False
|
|
338
|
+
except Exception:
|
|
339
|
+
results[filename] = False
|
|
340
|
+
|
|
341
|
+
# Проверяем файл приветственного сообщения
|
|
342
|
+
try:
|
|
343
|
+
if self.welcome_file.exists():
|
|
344
|
+
async with aiofiles.open(self.welcome_file, 'r', encoding='utf-8') as f:
|
|
345
|
+
content = await f.read()
|
|
346
|
+
results['welcome_message.txt'] = bool(content.strip() and len(content.strip()) > 5)
|
|
347
|
+
else:
|
|
348
|
+
results['welcome_message.txt'] = False
|
|
349
|
+
except Exception:
|
|
350
|
+
results['welcome_message.txt'] = False
|
|
351
|
+
|
|
352
|
+
# Проверяем файл справки (опционально)
|
|
353
|
+
try:
|
|
354
|
+
if self.help_file.exists():
|
|
355
|
+
async with aiofiles.open(self.help_file, 'r', encoding='utf-8') as f:
|
|
356
|
+
content = await f.read()
|
|
357
|
+
results['help_message.txt'] = bool(content.strip() and len(content.strip()) > 5)
|
|
358
|
+
else:
|
|
359
|
+
results['help_message.txt'] = False # Не критично
|
|
360
|
+
except Exception:
|
|
361
|
+
results['help_message.txt'] = False
|
|
362
|
+
|
|
363
|
+
# 🆕 Проверяем финальные инструкции (опционально)
|
|
364
|
+
try:
|
|
365
|
+
if self.final_instructions_file.exists():
|
|
366
|
+
async with aiofiles.open(self.final_instructions_file, 'r', encoding='utf-8') as f:
|
|
367
|
+
content = await f.read()
|
|
368
|
+
results['final_instructions.txt'] = bool(content.strip() and len(content.strip()) > 5)
|
|
369
|
+
else:
|
|
370
|
+
results['final_instructions.txt'] = False # Не критично - опциональный файл
|
|
371
|
+
except Exception:
|
|
372
|
+
results['final_instructions.txt'] = False
|
|
373
|
+
|
|
374
|
+
return results
|
|
375
|
+
|
|
376
|
+
def get_prompt_info(self) -> Dict[str, any]:
|
|
377
|
+
"""Возвращает информацию о конфигурации промптов"""
|
|
378
|
+
return {
|
|
379
|
+
'prompts_dir': str(self.prompts_dir),
|
|
380
|
+
'prompt_files': self.prompt_files,
|
|
381
|
+
'welcome_file': 'welcome_message.txt',
|
|
382
|
+
'help_file': 'help_message.txt',
|
|
383
|
+
'final_instructions_file': 'final_instructions.txt', # 🆕
|
|
384
|
+
'total_files': len(self.prompt_files) + 1, # +1 для welcome message
|
|
385
|
+
'json_instructions_included': True
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
async def test_json_parsing(self, test_response: str) -> Dict[str, any]:
|
|
389
|
+
"""Тестирует парсинг JSON из ответа ИИ (для отладки)"""
|
|
390
|
+
import json
|
|
391
|
+
import re
|
|
392
|
+
|
|
393
|
+
try:
|
|
394
|
+
# Используем тот же алгоритм что и в main.py
|
|
395
|
+
json_pattern = r'\{[^{}]*"этап"[^{}]*\}$'
|
|
396
|
+
match = re.search(json_pattern, test_response.strip())
|
|
397
|
+
|
|
398
|
+
if match:
|
|
399
|
+
json_str = match.group(0)
|
|
400
|
+
response_text = test_response[:match.start()].strip()
|
|
401
|
+
|
|
402
|
+
try:
|
|
403
|
+
metadata = json.loads(json_str)
|
|
404
|
+
return {
|
|
405
|
+
'success': True,
|
|
406
|
+
'response_text': response_text,
|
|
407
|
+
'metadata': metadata,
|
|
408
|
+
'json_str': json_str
|
|
409
|
+
}
|
|
410
|
+
except json.JSONDecodeError as e:
|
|
411
|
+
return {
|
|
412
|
+
'success': False,
|
|
413
|
+
'error': f"JSON decode error: {e}",
|
|
414
|
+
'json_str': json_str
|
|
415
|
+
}
|
|
416
|
+
else:
|
|
417
|
+
return {
|
|
418
|
+
'success': False,
|
|
419
|
+
'error': "JSON pattern not found",
|
|
420
|
+
'response_text': test_response
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
except Exception as e:
|
|
424
|
+
return {
|
|
425
|
+
'success': False,
|
|
426
|
+
'error': f"Parse error: {e}"
|
|
427
|
+
}
|