Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -2,6 +2,8 @@ import gradio as gr
|
|
2 |
import pandas as pd
|
3 |
import os
|
4 |
import random
|
|
|
|
|
5 |
from openai import OpenAI
|
6 |
|
7 |
XAI_API_KEY = os.getenv("XAI_API_KEY")
|
@@ -16,7 +18,10 @@ def load_dropdown_data(file_path, sheet_name, column_name):
|
|
16 |
|
17 |
file_path = "Исходные данные.xlsx"
|
18 |
|
19 |
-
|
|
|
|
|
|
|
20 |
genders_data = pd.read_excel(file_path, sheet_name="Пол")
|
21 |
generations_data = pd.read_excel(file_path, sheet_name="Поколение")
|
22 |
psychotypes_data = pd.read_excel(file_path, sheet_name="Психотип")
|
@@ -49,15 +54,25 @@ approach_dict = {
|
|
49 |
}
|
50 |
|
51 |
def fill_product_details(selected_product, data):
|
52 |
-
|
53 |
-
|
54 |
-
return (
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
|
62 |
def get_approaches(gender, generation, psychotype, approaches_df):
|
63 |
if approaches_df is None or approaches_df.empty:
|
@@ -221,6 +236,114 @@ def call_model(model_prompt):
|
|
221 |
)
|
222 |
return completion.choices[0].message.content.strip()
|
223 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
def update_prompts_on_params_change(description, product_name, benefits, key_message,
|
225 |
gender, generation, psychotype, business_stage, industry, opf):
|
226 |
chosen_approach = get_approaches(gender, generation, psychotype, approaches_data)
|
@@ -232,10 +355,9 @@ def update_prompts_on_params_change(description, product_name, benefits, key_mes
|
|
232 |
def generate_personalized_sms(description, product_name, benefits, key_message,
|
233 |
gender, generation, psychotype, business_stage, industry, opf,
|
234 |
chosen_approach, prompt_1, prompt_2):
|
235 |
-
# Если нет персональных параметров или
|
236 |
if "Для формирования промпта выберите хотя бы один личный персональный параметр" in prompt_1 or chosen_approach == "Подход не найден для выбранных параметров.":
|
237 |
gr.Warning("Задайте хотя бы один личный персональный параметр для определения подхода, чтобы был сформирован промпт")
|
238 |
-
# Возвращаем пустые результаты для SMS
|
239 |
return "", ""
|
240 |
|
241 |
approach_list = [a.strip() for a in chosen_approach.split(',') if a.strip()]
|
@@ -253,8 +375,8 @@ def generate_personalized_sms(description, product_name, benefits, key_message,
|
|
253 |
gender, generation, psychotype, business_stage, industry, opf,
|
254 |
chosen_single_approach_2)
|
255 |
|
256 |
-
sms_1 =
|
257 |
-
sms_2 =
|
258 |
|
259 |
return sms_1, sms_2
|
260 |
|
@@ -272,11 +394,11 @@ with gr.Blocks(theme="default") as demo:
|
|
272 |
with gr.Row():
|
273 |
with gr.Column(scale=1):
|
274 |
gr.Markdown("**Продукт**")
|
275 |
-
product_dropdown = gr.Dropdown(label="Продукт", choices=products, value=
|
276 |
-
description = gr.Textbox(label="Описание предложения", lines=5, value="")
|
277 |
-
product_name = gr.Textbox(label="Наименование продукта", lines=1, value="")
|
278 |
-
benefits = gr.Textbox(label="Преимущества", lines=9, value="")
|
279 |
-
key_message = gr.Textbox(label="Ключевое сообщение", lines=2, value="")
|
280 |
|
281 |
product_dropdown.change(
|
282 |
fn=lambda selected: fill_product_details(selected, data_products),
|
@@ -292,7 +414,7 @@ with gr.Blocks(theme="default") as demo:
|
|
292 |
business_stage_dropdown = gr.Dropdown(label="Стадия бизнеса", choices=["Не выбрано"]+business_stages, value=None)
|
293 |
industry_dropdown = gr.Dropdown(label="Отрасль", choices=["Не выбрано"]+industries, value=None)
|
294 |
opf_dropdown = gr.Dropdown(label="ОПФ", choices=["Не выбрано"]+opfs, value=None)
|
295 |
-
chosen_approach = gr.Textbox(label="Выбранный подход", lines=1, value="")
|
296 |
presence_in_db = gr.Textbox(label="Комментарий", lines=1, value="", interactive=False)
|
297 |
|
298 |
with gr.Row():
|
@@ -304,12 +426,12 @@ with gr.Blocks(theme="default") as demo:
|
|
304 |
with gr.Column():
|
305 |
model_1_name = gr.Textbox(label="Модель 1", value="Grok-2-1212", interactive=False)
|
306 |
prompt_1 = gr.Textbox(label="Промпт 1", value="", interactive=False, lines=10)
|
307 |
-
sms_1 = gr.Textbox(label="SMS 1", lines=3, value="")
|
308 |
|
309 |
with gr.Column():
|
310 |
model_2_name = gr.Textbox(label="Модель 2", value="Grok-2-1212", interactive=False)
|
311 |
prompt_2 = gr.Textbox(label="Промпт 2", value="", interactive=False, lines=10)
|
312 |
-
sms_2 = gr.Textbox(label="SMS 2", lines=3, value="")
|
313 |
|
314 |
with gr.Row():
|
315 |
prefer_sms_1_btn = gr.Button("Я предпочитаю это SMS")
|
@@ -341,9 +463,8 @@ with gr.Blocks(theme="default") as demo:
|
|
341 |
)
|
342 |
|
343 |
# Генерация SMS по нажатию кнопки
|
344 |
-
# Если персональные параметры не выбраны - выдаём gr.Warning
|
345 |
create_personal_sms_btn.click(
|
346 |
-
fn=
|
347 |
inputs=[description, product_name, benefits, key_message,
|
348 |
gender_dropdown, generation_dropdown, psychotype_dropdown,
|
349 |
business_stage_dropdown, industry_dropdown, opf_dropdown,
|
@@ -351,5 +472,4 @@ with gr.Blocks(theme="default") as demo:
|
|
351 |
outputs=[sms_1, sms_2]
|
352 |
)
|
353 |
|
354 |
-
# Обязательно включаем очередь:
|
355 |
demo.queue().launch()
|
|
|
2 |
import pandas as pd
|
3 |
import os
|
4 |
import random
|
5 |
+
import re
|
6 |
+
import pymorphy3
|
7 |
from openai import OpenAI
|
8 |
|
9 |
XAI_API_KEY = os.getenv("XAI_API_KEY")
|
|
|
18 |
|
19 |
file_path = "Исходные данные.xlsx"
|
20 |
|
21 |
+
products_list, data_products = load_dropdown_data(file_path, "Продукты", "Наименование продукта"), pd.read_excel(file_path, sheet_name="Продукты")
|
22 |
+
# Добавляем "Свой продукт" в начало списка
|
23 |
+
products = ["Свой продукт"] + list(products_list)
|
24 |
+
|
25 |
genders_data = pd.read_excel(file_path, sheet_name="Пол")
|
26 |
generations_data = pd.read_excel(file_path, sheet_name="Поколение")
|
27 |
psychotypes_data = pd.read_excel(file_path, sheet_name="Психотип")
|
|
|
54 |
}
|
55 |
|
56 |
def fill_product_details(selected_product, data):
|
57 |
+
# Если выбран "Свой продукт", поля очищаем и даём редактировать
|
58 |
+
if selected_product == "Свой продукт":
|
59 |
+
return gr.update(value="", interactive=True), \
|
60 |
+
gr.update(value="", interactive=True), \
|
61 |
+
gr.update(value="", interactive=True), \
|
62 |
+
gr.update(value="", interactive=True)
|
63 |
+
else:
|
64 |
+
# Иначе заполняем из файла и делаем поля неактивными
|
65 |
+
if selected_product and selected_product in data["Наименование продукта"].values:
|
66 |
+
product_row = data[data["Наименование продукта"] == selected_product].iloc[0]
|
67 |
+
return (gr.update(value=product_row.get("Описание предложения", ""), interactive=False),
|
68 |
+
gr.update(value=product_row.get("Наименование продукта", ""), interactive=False),
|
69 |
+
gr.update(value=product_row.get("Преимущества", ""), interactive=False),
|
70 |
+
gr.update(value=product_row.get("Ключевое сообщение", ""), interactive=False))
|
71 |
+
else:
|
72 |
+
return gr.update(value="", interactive=False), \
|
73 |
+
gr.update(value="", interactive=False), \
|
74 |
+
gr.update(value="", interactive=False), \
|
75 |
+
gr.update(value="", interactive=False)
|
76 |
|
77 |
def get_approaches(gender, generation, psychotype, approaches_df):
|
78 |
if approaches_df is None or approaches_df.empty:
|
|
|
236 |
)
|
237 |
return completion.choices[0].message.content.strip()
|
238 |
|
239 |
+
# Функция для корректировки
|
240 |
+
def correct_dash_usage(text):
|
241 |
+
morph = pymorphy3.MorphAnalyzer()
|
242 |
+
text = re.sub(r'\s[-–—]\s', ' — ', text)
|
243 |
+
text = re.sub(r'(?<=\d)[-–—](?=\d)', '–', text)
|
244 |
+
text = re.sub(r'(?<=[a-zA-Zа-яА-Я0-9])[-–—](?=[a-zA-Zа-яА-Я0-9])', '-', text)
|
245 |
+
text = re.sub(r'"([^\"]+)"', r'«\1»', text)
|
246 |
+
if text.count('"') == 1:
|
247 |
+
text = text.replace('"', '')
|
248 |
+
if (text.startswith('"') and text.endswith('"')) or (text.startswith('«') and text.endswith('»')):
|
249 |
+
text = text[1:-1].strip()
|
250 |
+
text = re.sub(r'(\d+)[kкКK]', r'\1 000', text, flags=re.IGNORECASE)
|
251 |
+
|
252 |
+
greeting_patterns = [
|
253 |
+
r"привет\b", r"здравствуй", r"добрый\s(день|вечер|утро)",
|
254 |
+
r"дорогой\b", r"уважаемый\b", r"дорогая\b", r"уважаемая\b",
|
255 |
+
r"господин\b", r"госпожа\b", r"друг\b", r"коллега\b",
|
256 |
+
r"товарищ\b", r"приятель\b", r"подруга\b"
|
257 |
+
]
|
258 |
+
def is_greeting_sentence(sentence):
|
259 |
+
words = sentence.split()
|
260 |
+
if len(words) < 5:
|
261 |
+
for word in words:
|
262 |
+
parsed = morph.parse(word.lower())[0]
|
263 |
+
for pattern in greeting_patterns:
|
264 |
+
if re.search(pattern, parsed.normal_form):
|
265 |
+
return True
|
266 |
+
return False
|
267 |
+
|
268 |
+
sentences = re.split(r'(?<=[.!?])\s+', text)
|
269 |
+
if sentences and is_greeting_sentence(sentences[0]):
|
270 |
+
sentences = sentences[1:]
|
271 |
+
text = ' '.join(sentences)
|
272 |
+
|
273 |
+
def restore_yo(text):
|
274 |
+
morph2 = pymorphy3.MorphAnalyzer()
|
275 |
+
words = text.split()
|
276 |
+
restored_words = []
|
277 |
+
for word in words:
|
278 |
+
if word.isupper():
|
279 |
+
restored_words.append(word)
|
280 |
+
continue
|
281 |
+
if word.lower() == "все":
|
282 |
+
restored_words.append(word)
|
283 |
+
continue
|
284 |
+
parsed = morph2.parse(word)[0]
|
285 |
+
restored_word = parsed.word
|
286 |
+
if word and word[0].isupper():
|
287 |
+
restored_word = restored_word.capitalize()
|
288 |
+
restored_words.append(restored_word)
|
289 |
+
return ' '.join(restored_words)
|
290 |
+
|
291 |
+
text = restore_yo(text)
|
292 |
+
text = re.sub(r'\bИп\b', 'ИП', text, flags=re.IGNORECASE)
|
293 |
+
text = re.sub(r'\bОоо\b', 'ООО', text, flags=re.IGNORECASE)
|
294 |
+
text = re.sub(r'\bРф\b', 'РФ', text, flags=re.IGNORECASE)
|
295 |
+
text = re.sub(r'\bпользовуйтесь\b', 'пользуйтесь', text, flags=re.IGNORECASE)
|
296 |
+
text = re.sub(r'\bею\b', 'ей', text, flags=re.IGNORECASE)
|
297 |
+
text = re.sub(r'\bповышьте\b', 'повысьте', text, flags=re.IGNORECASE)
|
298 |
+
text = re.sub(r'\bСбербизнес(?:а|е)?\b', 'СберБизнес', text, flags=re.IGNORECASE)
|
299 |
+
text = re.sub(r'\bСбербанк\b', 'СберБанк', text, flags=re.IGNORECASE)
|
300 |
+
text = re.sub(r'\bвашего ООО\b', 'вашей компании', text, flags=re.IGNORECASE)
|
301 |
+
text = re.sub(r'\b0₽\b', '0 р', text, flags=re.IGNORECASE)
|
302 |
+
text = re.sub(r'\b₽\b', 'р', text, flags=re.IGNORECASE)
|
303 |
+
text = re.sub(r'\bруб\.(?=\W|$)', 'р', text, flags=re.IGNORECASE)
|
304 |
+
text = re.sub(r'\bруб(?:ля|лей)\b', 'р', text, flags=re.IGNORECASE)
|
305 |
+
text = re.sub(r'(\d+)\s+тысяч(?:а|и)?(?:\s+рублей)?', r'\1 000 р', text, flags=re.IGNORECASE)
|
306 |
+
text = re.sub(r'(\d+)\s*тыс\.\s*руб\.', r'\1 000 р', text, flags=re.IGNORECASE)
|
307 |
+
text = re.sub(r'(\d+)\s*тыс\.\s*р\.?', r'\1 000 р', text, flags=re.IGNORECASE)
|
308 |
+
text = re.sub(r'(\d+)\s+миллиона\b|\bмиллионов\b', r'\1 млн', text, flags=re.IGNORECASE)
|
309 |
+
text = re.sub(r'(\d+)\s*млн\s*руб\.', r'\1 млн р', text, flags=re.IGNORECASE)
|
310 |
+
text = re.sub(r'(\d+)\s*р\b', r'\1 р', text)
|
311 |
+
|
312 |
+
def remove_specific_sentences(text):
|
313 |
+
sents = re.split(r'(?<=[.!?])\s+', text)
|
314 |
+
filtered = [s for s in sents if not re.search(r'\bникаких\s+(посещений|визитов)\b', s, re.IGNORECASE)]
|
315 |
+
return ' '.join(filtered)
|
316 |
+
|
317 |
+
text = re.sub(r'\b(\d+)\s+000\s+000\s*р\b', r'\1 млн р', text, flags=re.IGNORECASE)
|
318 |
+
text = re.sub(r' р р ', r' р ', text, flags=re.IGNORECASE)
|
319 |
+
text = remove_specific_sentences(text)
|
320 |
+
return text
|
321 |
+
|
322 |
+
def clean_message(message):
|
323 |
+
if not message.endswith(('.', '!', '?')):
|
324 |
+
last_period = max(message.rfind('.'), message.rfind('!'), message.rfind('?'))
|
325 |
+
if last_period != -1:
|
326 |
+
message = message[:last_period + 1]
|
327 |
+
return message
|
328 |
+
|
329 |
+
def generate_message_with_retry(model_prompt):
|
330 |
+
# До 10 попыток, чтобы длина была от 160 до 250 символов
|
331 |
+
last_message = ""
|
332 |
+
for _ in range(10):
|
333 |
+
msg = call_model(model_prompt)
|
334 |
+
msg = correct_dash_usage(msg)
|
335 |
+
msg = clean_message(msg)
|
336 |
+
length = len(msg)
|
337 |
+
if 160 <= length <= 250:
|
338 |
+
# Добавляем информацию о количестве знаков
|
339 |
+
msg += f"\n\n------\nКоличество знаков: {length}"
|
340 |
+
return msg
|
341 |
+
last_message = msg
|
342 |
+
# Если не удалось подобрать длину
|
343 |
+
length = len(last_message)
|
344 |
+
last_message += f"\n\n------\nКоличество знаков: {length}"
|
345 |
+
return last_message
|
346 |
+
|
347 |
def update_prompts_on_params_change(description, product_name, benefits, key_message,
|
348 |
gender, generation, psychotype, business_stage, industry, opf):
|
349 |
chosen_approach = get_approaches(gender, generation, psychotype, approaches_data)
|
|
|
355 |
def generate_personalized_sms(description, product_name, benefits, key_message,
|
356 |
gender, generation, psychotype, business_stage, industry, opf,
|
357 |
chosen_approach, prompt_1, prompt_2):
|
358 |
+
# Если нет персональных параметров или подхода
|
359 |
if "Для формирования промпта выберите хотя бы один личный персональный параметр" in prompt_1 or chosen_approach == "Подход не найден для выбранных параметров.":
|
360 |
gr.Warning("Задайте хотя бы один личный персональный параметр для определения подхода, чтобы был сформирован промпт")
|
|
|
361 |
return "", ""
|
362 |
|
363 |
approach_list = [a.strip() for a in chosen_approach.split(',') if a.strip()]
|
|
|
375 |
gender, generation, psychotype, business_stage, industry, opf,
|
376 |
chosen_single_approach_2)
|
377 |
|
378 |
+
sms_1 = generate_message_with_retry(model_prompt_1)
|
379 |
+
sms_2 = generate_message_with_retry(model_prompt_2)
|
380 |
|
381 |
return sms_1, sms_2
|
382 |
|
|
|
394 |
with gr.Row():
|
395 |
with gr.Column(scale=1):
|
396 |
gr.Markdown("**Продукт**")
|
397 |
+
product_dropdown = gr.Dropdown(label="Продукт", choices=products, value=products[0])
|
398 |
+
description = gr.Textbox(label="Описание предложения", lines=5, value="", interactive=True)
|
399 |
+
product_name = gr.Textbox(label="Наименование продукта", lines=1, value="", interactive=True)
|
400 |
+
benefits = gr.Textbox(label="Преимущества", lines=9, value="", interactive=True)
|
401 |
+
key_message = gr.Textbox(label="Ключевое сообщение", lines=2, value="", interactive=True)
|
402 |
|
403 |
product_dropdown.change(
|
404 |
fn=lambda selected: fill_product_details(selected, data_products),
|
|
|
414 |
business_stage_dropdown = gr.Dropdown(label="Стадия бизнеса", choices=["Не выбрано"]+business_stages, value=None)
|
415 |
industry_dropdown = gr.Dropdown(label="Отрасль", choices=["Не выбрано"]+industries, value=None)
|
416 |
opf_dropdown = gr.Dropdown(label="ОПФ", choices=["Не выбрано"]+opfs, value=None)
|
417 |
+
chosen_approach = gr.Textbox(label="Выбранный подход", lines=1, value="", interactive=False)
|
418 |
presence_in_db = gr.Textbox(label="Комментарий", lines=1, value="", interactive=False)
|
419 |
|
420 |
with gr.Row():
|
|
|
426 |
with gr.Column():
|
427 |
model_1_name = gr.Textbox(label="Модель 1", value="Grok-2-1212", interactive=False)
|
428 |
prompt_1 = gr.Textbox(label="Промпт 1", value="", interactive=False, lines=10)
|
429 |
+
sms_1 = gr.Textbox(label="SMS 1", lines=3, value="", interactive=False)
|
430 |
|
431 |
with gr.Column():
|
432 |
model_2_name = gr.Textbox(label="Модель 2", value="Grok-2-1212", interactive=False)
|
433 |
prompt_2 = gr.Textbox(label="Промпт 2", value="", interactive=False, lines=10)
|
434 |
+
sms_2 = gr.Textbox(label="SMS 2", lines=3, value="", interactive=False)
|
435 |
|
436 |
with gr.Row():
|
437 |
prefer_sms_1_btn = gr.Button("Я предпочитаю это SMS")
|
|
|
463 |
)
|
464 |
|
465 |
# Генерация SMS по нажатию кнопки
|
|
|
466 |
create_personal_sms_btn.click(
|
467 |
+
fn=generate_personal_sms,
|
468 |
inputs=[description, product_name, benefits, key_message,
|
469 |
gender_dropdown, generation_dropdown, psychotype_dropdown,
|
470 |
business_stage_dropdown, industry_dropdown, opf_dropdown,
|
|
|
472 |
outputs=[sms_1, sms_2]
|
473 |
)
|
474 |
|
|
|
475 |
demo.queue().launch()
|