Spaces:
Sleeping
Sleeping
import gradio as gr | |
import pandas as pd | |
import os | |
import random | |
import re | |
import pymorphy3 | |
import requests | |
import json | |
import base64 | |
import time | |
from openai import OpenAI | |
XAI_API_KEY = os.getenv("XAI_API_KEY") | |
client = OpenAI( | |
api_key=XAI_API_KEY, | |
base_url="https://api.x.ai/v1", | |
) | |
token = os.getenv('GITHUB_TOKEN') | |
repo = "fruitpicker01/Storage_Anastasia" | |
current_request_index = -1 | |
def load_dropdown_data(file_path, sheet_name, column_name): | |
data = pd.read_excel(file_path, sheet_name=sheet_name) | |
return data[column_name].dropna().unique().tolist() | |
file_path = "Исходные данные.xlsx" | |
products_list, data_products = load_dropdown_data(file_path, "Продукты", "Наименование продукта"), pd.read_excel(file_path, sheet_name="Продукты") | |
products = ["Свой продукт"] + list(products_list) | |
genders_data = pd.read_excel(file_path, sheet_name="Пол") | |
generations_data = pd.read_excel(file_path, sheet_name="Поколение") | |
psychotypes_data = pd.read_excel(file_path, sheet_name="Психотип") | |
business_stages_data = pd.read_excel(file_path, sheet_name="Стадия бизнеса") | |
industries_data = pd.read_excel(file_path, sheet_name="Отрасль") | |
opfs_data = pd.read_excel(file_path, sheet_name="ОПФ") | |
genders = genders_data["Пол"].dropna().unique().tolist() | |
generations = generations_data["Поколение"].dropna().unique().tolist() | |
psychotypes = psychotypes_data["Психотип"].dropna().unique().tolist() | |
business_stages = business_stages_data["Стадия бизнеса"].dropna().unique().tolist() | |
industries = industries_data["Отрасль"].dropna().unique().tolist() | |
opfs = opfs_data["ОПФ"].dropna().unique().tolist() | |
approaches_data = pd.read_excel(file_path, sheet_name="Подход") | |
approach_dict = { | |
"Указание на пользу": { | |
"prefix": "Начни SMS с указания на пользу продукта. Используй глагол в побудительном наклонении. Не начинай с вопроса", | |
"suffix": "Убедись, что SMS начинается с указания на пользу продукта и использования глагола в побудительном наклонении и не начинается с вопроса" | |
}, | |
"Вопрос": { | |
"prefix": "Начни сообщение с вопроса, который указывает на пользу продукта для клиента", | |
"suffix": "Убедись, что готовый текст начинается с вопроса, который указывает на пользу продукта для клиента" | |
}, | |
"Призыв к действию": { | |
"prefix": "Начни SMS с призыва к действию с продуктом. Не начинай с вопроса", | |
"suffix": "Убедись, что готовый текст начинается с призыва к действию с продуктом и не начинается с вопроса" | |
} | |
} | |
def fill_product_details(selected_product, data): | |
if selected_product == "Свой продукт": | |
return (gr.update(value="", interactive=True), | |
gr.update(value="", interactive=True), | |
gr.update(value="", interactive=True), | |
gr.update(value="", interactive=True)) | |
else: | |
if selected_product and selected_product in data["Наименование продукта"].values: | |
product_row = data[data["Наименование продукта"] == selected_product].iloc[0] | |
return (gr.update(value=product_row.get("Описание предложения", ""), interactive=False), | |
gr.update(value=product_row.get("Наименование продукта", ""), interactive=False), | |
gr.update(value=product_row.get("Преимущества", ""), interactive=False), | |
gr.update(value=product_row.get("Ключевое сообщение", ""), interactive=False)) | |
else: | |
return (gr.update(value="", interactive=False), | |
gr.update(value="", interactive=False), | |
gr.update(value="", interactive=False), | |
gr.update(value="", interactive=False)) | |
def get_approaches(gender, generation, psychotype, approaches_df): | |
if approaches_df is None or approaches_df.empty: | |
return "Подход не найден для выбранных параметров." | |
filters = [] | |
for param_name, param_value in [('Пол', gender), ('Поколение', generation), ('Психотип', psychotype)]: | |
if not param_value or param_value == "Не выбрано": | |
filters.append(approaches_df[param_name].isnull() | (approaches_df[param_name].fillna('') == '')) | |
else: | |
filters.append(approaches_df[param_name].fillna('') == param_value) | |
combined_filter = filters[0] | |
for f in filters[1:]: | |
combined_filter &= f | |
matching_rows = approaches_df[combined_filter] | |
if matching_rows.empty: | |
return "Подход не найден для выбранных параметров." | |
approach_list = [] | |
for approaches in matching_rows['Подход']: | |
approach_names = [a.strip() for a in str(approaches).split(',')] | |
approach_list.extend(approach_names) | |
approach_list = list(set(approach_list)) | |
return ', '.join(approach_list) | |
def get_instructions_for_param(param_value, df, col): | |
if not param_value or param_value == "Не выбрано": | |
return None | |
row = df[df[col] == param_value] | |
if row.empty: | |
return None | |
instr1 = row.iloc[0].get("Инструкция 1", "") | |
if not instr1.strip(): | |
return None | |
return instr1 | |
def format_instruction_string(instr): | |
terms = [t.strip() for t in instr.split(',') if t.strip()] | |
return " или ".join(terms) if terms else "" | |
def generate_display_prompts(description, product_name, benefits, key_message, chosen_approach, | |
gender, generation, psychotype, business_stage, industry, opf): | |
if chosen_approach == "Подход не найден для выбранных параметров.": | |
return ("Для формирования промпта выберите хотя бы один личный персональный параметр для определения подхода", | |
"Для формирования промпта выберите хотя бы один личный персональный параметр для определения подхода") | |
approach_list = [a.strip() for a in chosen_approach.split(',') if a.strip()] | |
prefix_parts = [] | |
suffix_parts = [] | |
for a in approach_list: | |
if a in approach_dict: | |
prefix_parts.append(approach_dict[a]["prefix"]) | |
suffix_parts.append(approach_dict[a]["suffix"]) | |
if len(prefix_parts) > 1: | |
approach_prefix = " / ".join(prefix_parts) | |
approach_suffix = " / ".join(suffix_parts) | |
else: | |
approach_prefix = prefix_parts[0] if prefix_parts else "" | |
approach_suffix = suffix_parts[0] if suffix_parts else "" | |
instructions_data = [ | |
(gender, genders_data, "Пол"), | |
(generation, generations_data, "Поколение"), | |
(psychotype, psychotypes_data, "Психотип"), | |
(business_stage, business_stages_data, "Стадия бизнеса"), | |
(industry, industries_data, "Отрасль"), | |
(opf, opfs_data, "ОПФ") | |
] | |
chosen_params_instructions = [] | |
for (param_value, df, col) in instructions_data: | |
instr1 = get_instructions_for_param(param_value, df, col) | |
if instr1: | |
chosen_params_instructions.append(instr1) | |
if not chosen_params_instructions: | |
return ("Для формирования промпта выберите хотя бы один личный персональный параметр для определения подхода", | |
"Для формирования промпта выберите хотя бы один личный персональный параметр для определения подхода") | |
lines = [] | |
for i, instr_line in enumerate(chosen_params_instructions, start=1): | |
formatted_line = format_instruction_string(instr_line) | |
lines.append(f"{i}. {formatted_line}.") | |
mandatory_terms = "\n".join(lines) | |
extra_line = "" | |
if generation == "Z": | |
extra_line = "Обратись в SMS на ты. " | |
prompt_1 = f"""Напиши три или четыре предложения суммарной длиной от 160 до 250 знаков с учетом пробелов. {approach_prefix}. | |
{extra_line}Напиши рекламное SMS для следующего продукта: | |
«{description}». | |
Не изменяй название продукта: «{product_name}». | |
Преимущества: | |
«{benefits}». | |
ОБЯЗАТЕЛЬНО используй в SMS КАЖДЫЙ из следующих терминов, касающиеся клиента, которому направляется SMS: | |
{mandatory_terms} | |
Убедись, что написал не меньше трех и не больше четырех предложений суммарной длиной от 160 до 250 знаков с учетом пробелов. | |
{approach_suffix}. | |
Убедись, что УМЕСТНО использовал КАЖДЫЙ необходимый термин. | |
Убедись, что в SMS без изменений, синонимов и перестановок слов используется наименование продукта: «{product_name}». | |
Убедись, что в SMS есть следующая ключевая информация: «{key_message}».""" | |
prompt_2 = f"""Напиши три или четыре предложения суммарной длиной от 160 до 250 знаков с учетом пробелов. {approach_prefix}. | |
{extra_line}Напиши рекламное SMS для следующего продукта: | |
«{description}». | |
Не изменяй название продукта: «{product_name}». | |
Преимущества: | |
«{benefits}». | |
Используй в SMS КАЖДЫЙ из следующих терминов, касающиеся клиента, которому направляется SMS: | |
{mandatory_terms} | |
Убедись, что написал не меньше трех и не больше четырех предложений суммарной длиной от 160 до 250 знаков с учетом пробелов. | |
{approach_suffix}. | |
Убедись, что УМЕСТНО использовал КАЖДЫЙ необходимый термин. | |
Убедись, что в SMS без изменений, синонимов и перестановок слов используется наименование продукта: «{product_name}». | |
Убедись, что в SMS есть следующая ключевая информация: «{key_message}».""" | |
return prompt_1, prompt_2 | |
def call_model(model_prompt): | |
completion = client.chat.completions.create( | |
model="grok-2-1212", | |
messages=[ | |
{"role": "system", "content": "You are a world-class expert in creating personalized SMS who returns only the SMS and nothing else."}, | |
{"role": "user", "content": model_prompt}, | |
], | |
) | |
return completion.choices[0].message.content.strip() | |
def correct_dash_usage(text): | |
return text | |
def clean_message(message): | |
if not message.endswith(('.', '!', '?')): | |
last_period = max(message.rfind('.'), message.rfind('!'), message.rfind('?')) | |
if last_period != -1: | |
message = message[:last_period + 1] | |
return message | |
def tokenize_words(text): | |
""" | |
Разбивает текст на слова, игнорируя знаки препинания. | |
""" | |
return re.findall(r'\w+', text, re.UNICODE) | |
def normalize(word): | |
""" | |
Возвращает начальную форму слова с помощью pymorphy3. | |
Приводит к нижнему регистру для унификации. | |
""" | |
morph = pymorphy3.MorphAnalyzer() | |
parsed = morph.parse(word) | |
if parsed: | |
return parsed[0].normal_form.lower() | |
return word.lower() | |
def find_word_matches(normalized_msg, normalized_prod): | |
""" | |
Находит индексы начала совпадений названия продукта в нормализованных словах. | |
""" | |
matches = [] | |
prod_len = len(normalized_prod) | |
for i in range(len(normalized_msg) - prod_len + 1): | |
window = normalized_msg[i:i+prod_len] | |
if window == normalized_prod: | |
matches.append(i) | |
return matches | |
def get_word_positions(message): | |
""" | |
Возвращает список кортежей (слово, start_index, end_index) для каждого слова в сообщении. | |
""" | |
word_positions = [] | |
for match in re.finditer(r'\w+', message): | |
word = match.group(0) | |
start = match.start() | |
end = match.end() | |
word_positions.append((word, start, end)) | |
return word_positions | |
def capitalize_sentences(text): | |
""" | |
Капитализирует первую букву каждого предложения в тексте. | |
Предложения считаются разделенными точками, восклицательными или вопросительными знаками. | |
""" | |
# Разделяем текст на предложения | |
sentence_endings = re.compile(r'([.!?])') | |
parts = sentence_endings.split(text) | |
# Объединяем разделенные части и капитализируем первые буквы | |
sentences = [] | |
for i in range(0, len(parts)-1, 2): | |
sentence = parts[i].strip() | |
punctuation = parts[i+1] | |
if sentence: | |
sentence = sentence[0].upper() + sentence[1:] | |
sentences.append(sentence + punctuation) | |
# Обработка возможного остатка текста без завершающего знака | |
if len(parts) % 2 != 0 and parts[-1].strip(): | |
last_sentence = parts[-1].strip() | |
last_sentence = last_sentence[0].upper() + last_sentence[1:] | |
sentences.append(last_sentence) | |
# Объединяем обратно в текст | |
return ' '.join(sentences) | |
def process_message(message, product_name): | |
""" | |
Обрабатывает сообщение, заменяя название продукта. | |
- Первое слово сохраняется в инфлектированной форме, как в сообщении. | |
- Остальные слова заменяются на оригинальные слова из названия продукта, сохраняя их капитализацию. | |
Возвращает обработанное сообщение. | |
""" | |
# Токенизация сообщения (без пунктуации) | |
message_words = tokenize_words(message) | |
normalized_message = [normalize(word) for word in message_words] | |
# Токенизация названия продукта | |
product_words_original = tokenize_words(product_name) # Оригинальные слова с капитализацией | |
normalized_product = [normalize(word) for word in product_words_original] | |
# Поиск совпадений | |
matches = find_word_matches(normalized_message, normalized_product) | |
if not matches: | |
# Если совпадений нет, вернуть исходное сообщение с капитализацией предложений | |
return message | |
# Получаем позиции всех слов в сообщении | |
word_positions = get_word_positions(message) | |
# Обработка каждого совпадения | |
# Для избежания смещения индексов при множественных заменах, обрабатываем с конца | |
matches_sorted = sorted(matches, reverse=True) | |
final_message = message | |
for match in matches_sorted: | |
# Индексы слов | |
start_word_idx = match | |
end_word_idx = match + len(product_words_original) - 1 | |
# Проверка, чтобы индексы не выходили за пределы списка | |
if end_word_idx >= len(word_positions): | |
continue # Пропускаем некорректные совпадения | |
# Получаем позиции слов | |
start_char = word_positions[start_word_idx][1] | |
end_char = word_positions[end_word_idx][2] | |
# Извлечение изменяемой части | |
matched_substring = final_message[start_char:end_char] | |
# Извлечение неизменяемой части | |
before = final_message[:start_char] | |
after = final_message[end_char:] | |
# Разделяем изменяемую часть на слова | |
words = matched_substring.replace('«', '').replace('»', '').strip().split() | |
if len(words) < len(product_words_original): | |
# Несоответствие количества слов, пропускаем замену | |
continue | |
# Сохраняем первое слово как есть (инфлектированное) | |
first_word = words[0] | |
clean_words = [] | |
for i in range(len(product_words_original[1:])): | |
if product_words_original[1:][i] == "Карта": | |
clean_words.append(words[i+1]) | |
else: | |
clean_words.append(product_words_original[1:][i]) | |
# Остальные слова берем из оригинального названия продукта | |
replaced_words = [first_word] + clean_words | |
# Собираем обратно измененную часть | |
processed = ' '.join(replaced_words) | |
# Воссоединяем части сообщения | |
final_message = before + processed + after | |
# Удаляем лишние пробелы | |
final_message = re.sub(r'\s+', ' ', final_message).strip() | |
# Капитализируем предложения | |
final_message = capitalize_sentences(final_message) | |
return final_message | |
def generate_message_with_retry(model_prompt, product_name): | |
last_message = "" | |
for _ in range(10): | |
msg = call_model(model_prompt) | |
msg = correct_dash_usage(msg) | |
msg = clean_message(msg) | |
msg = process_message(msg, product_name) | |
length = len(msg) | |
if 160 <= length <= 250: | |
msg += f"\n\n------\nКоличество знаков: {length}" | |
return msg | |
last_message = msg | |
length = len(last_message) | |
last_message += f"\n\n------\nКоличество знаков: {length}" | |
return last_message | |
def update_prompts_on_params_change(description, product_name, benefits, key_message, | |
gender, generation, psychotype, business_stage, industry, opf): | |
chosen_approach = get_approaches(gender, generation, psychotype, approaches_data) | |
prompt_1, prompt_2 = generate_display_prompts(description, product_name, benefits, key_message, | |
chosen_approach, gender, generation, psychotype, | |
business_stage, industry, opf) | |
return chosen_approach, prompt_1, prompt_2 | |
def save_user_request_to_github(selected_product, description, product_name, benefits, key_message, approach, personalization_params): | |
global current_request_index | |
current_request_index = -1 | |
data_to_save = { | |
"selected_product": selected_product, | |
"description": description, | |
"product_name": product_name, | |
"benefits": benefits, | |
"key_message": key_message, | |
"approach": approach, | |
"personalization_params": personalization_params, | |
"timestamp": time.time() | |
} | |
file_content_encoded = base64.b64encode(json.dumps(data_to_save).encode()).decode() | |
path = f"user_request_{int(time.time())}.json" | |
url = f"https://api.github.com/repos/{repo}/contents/{path}" | |
headers = { | |
"Authorization": f"token {token}", | |
"Content-Type": "application/json" | |
} | |
data = { | |
"message": f"Добавлен новый файл {path}", | |
"content": file_content_encoded | |
} | |
requests.put(url, headers=headers, data=json.dumps(data)) | |
def load_previous_user_request_from_github(): | |
global current_request_index | |
url = f"https://api.github.com/repos/{repo}/contents" | |
headers = { | |
"Authorization": f"token {token}", | |
"Content-Type": "application/json" | |
} | |
response = requests.get(url, headers=headers) | |
if response.status_code == 200: | |
files = response.json() | |
json_files = [file for file in files if file['name'].startswith("user_request_")] | |
if not json_files: | |
return products[0], "", "", "", "", "", None, None, None, None, None, "", "", "", "", "", "" | |
current_request_index -= 1 | |
if abs(current_request_index) > len(json_files): | |
current_request_index = -len(json_files) | |
target_file = json_files[current_request_index] | |
file_url = target_file['download_url'] | |
file_response = requests.get(file_url) | |
if file_response.status_code == 200: | |
data = json.loads(file_response.text) | |
selected_product = data.get('selected_product', products[0]) | |
description = data.get('description', "") | |
product_name = data.get('product_name', "") | |
benefits = data.get('benefits', "") | |
key_message = data.get('key_message', "") | |
approach = data.get('approach', "") | |
personalization_params = data.get('personalization_params', [None]*6) | |
if len(personalization_params) < 6: | |
personalization_params += [None]*(6-len(personalization_params)) | |
return (selected_product, description, product_name, benefits, key_message, approach, *personalization_params) | |
else: | |
return products[0], "", "", "", "", "", None, None, None, None, None, "", "", "", "", "", "" | |
else: | |
return products[0], "", "", "", "", "", None, None, None, None, None, "", "", "", "", "", "" | |
def generate_final_prompt_from_display(prompt_text, single_approach, is_prompt_1=True): | |
chosen_prefix = approach_dict[single_approach]["prefix"] | |
chosen_suffix = approach_dict[single_approach]["suffix"] | |
for approach in approach_dict: | |
if approach != single_approach: | |
other_prefix = approach_dict[approach]["prefix"] | |
other_suffix = approach_dict[approach]["suffix"] | |
prompt_text = prompt_text.replace(other_prefix, "") | |
prompt_text = prompt_text.replace(other_suffix, "") | |
prompt_text = prompt_text.replace(" / ", " ") | |
prompt_text = re.sub(r"\s{2,}", " ", prompt_text).strip() | |
if chosen_prefix not in prompt_text: | |
prompt_text = re.sub(r"с учетом пробелов\. [^.\n]*\.", f"с учетом пробелов. {chosen_prefix}.", prompt_text) | |
if chosen_suffix not in prompt_text: | |
prompt_text = re.sub(r"\n[^\n]*Убедись, что УМЕСТНО использовал КАЖДЫЙ необходимый термин.\n[^\n]*Убедись, что в SMS без.*\n[^\n]*Убедись, что в SMS есть следующая ключевая информация:", | |
f"\n{chosen_suffix}.\nУбедись, что УМЕСТНО использовал КАЖДЫЙ необходимый термин.\nУбедись, что в SMS без изменений, синонимов и перестановок слов используется наименование продукта:\nУбедись, что в SMS есть следующая ключевая информация:", prompt_text, flags=re.DOTALL) | |
prompt_text = re.sub(r"\s+([.,!?])", r"\1", prompt_text) | |
return prompt_text | |
final_prompt_1_state = gr.State("") | |
final_prompt_2_state = gr.State("") | |
def generate_personalized_sms_wrapper(selected_product, description, product_name, benefits, key_message, | |
gender, generation, psychotype, business_stage, industry, opf, | |
chosen_approach, prompt_1, prompt_2): | |
if "Для формирования промпта выберите хотя бы один личный персональный параметр" in prompt_1 or chosen_approach == "Подход не найден для выбранных параметров.": | |
gr.Warning("Задайте хотя бы один личный персональный параметр для определения подхода") | |
return "", "", "", "" | |
approach_list = [a.strip() for a in chosen_approach.split(',') if a.strip()] | |
if not approach_list: | |
gr.Warning("Задайте хотя бы один личный персональный параметр для определения подхода") | |
return "", "", "", "" | |
chosen_single_approach_1 = random.choice(approach_list) if len(approach_list) > 1 else approach_list[0] | |
chosen_single_approach_2 = random.choice(approach_list) if len(approach_list) > 1 else approach_list[0] | |
final_prompt_1 = generate_final_prompt_from_display(prompt_1, chosen_single_approach_1, is_prompt_1=True) | |
final_prompt_2 = generate_final_prompt_from_display(prompt_2, chosen_single_approach_2, is_prompt_1=False) | |
print("Final Prompt 1:", final_prompt_1) | |
print("Final Prompt 2:", final_prompt_2) | |
sms_1 = generate_message_with_retry(final_prompt_1, product_name) | |
sms_2 = generate_message_with_retry(final_prompt_2, product_name) | |
personalization_params = [gender, generation, psychotype, business_stage, industry, opf] | |
save_user_request_to_github(selected_product, description, product_name, benefits, key_message, chosen_approach, personalization_params) | |
return sms_1, sms_2, final_prompt_1, final_prompt_2 | |
def on_regenerate( | |
selected_product, description, product_name, benefits, key_message, | |
gender, generation, psychotype, business_stage, industry, opf, | |
chosen_approach, presence_in_db, | |
model_1_name, prompt_1, final_prompt_1, sms_1, | |
model_2_name, prompt_2, final_prompt_2, sms_2 | |
): | |
""" | |
Функция для кнопки «Перегенерировать SMS (не нравится ни одно из SMS)». | |
1) Перегенерирует sms_1, sms_2 с помощью final_prompt_1, final_prompt_2 и product_name. | |
2) Сохраняет все те же данные, что и on_prefer_sms_1/2, но с chosen_sms="none". | |
3) Возвращает новые sms_1, sms_2 для обновления интерфейса. | |
""" | |
# Перегенерируем SMS (аналогично regen_sms) | |
if not final_prompt_1.strip() or not final_prompt_2.strip(): | |
gr.Warning("Нечего перегенерировать, сначала создайте SMS.") | |
return "", "" | |
print("Regen Final Prompt 1:", final_prompt_1) | |
print("Regen Final Prompt 2:", final_prompt_2) | |
sms_1 = generate_message_with_retry(final_prompt_1, product_name) | |
sms_2 = generate_message_with_retry(final_prompt_2, product_name) | |
# Теперь сохраняем всё, как при «Я предпочитаю это SMS», | |
# только chosen_sms="none" | |
save_preferred_sms_to_github( | |
selected_product, description, product_name, benefits, key_message, | |
gender, generation, psychotype, business_stage, industry, opf, | |
chosen_approach, presence_in_db, | |
model_1_name, prompt_1, final_prompt_1, sms_1, | |
model_2_name, prompt_2, final_prompt_2, sms_2, | |
chosen_sms="none" # <-- признак, что ни одно SMS не выбрано | |
) | |
return sms_1, sms_2 | |
def on_load_previous(): | |
loaded_data = load_previous_user_request_from_github() | |
if not loaded_data or len(loaded_data) < 11: | |
return (products[0], "", "", "", "", None, None, None, None, None, None, "", "", "", "", "", "") | |
selected_product_val, description_val, product_name_val, benefits_val, key_message_val, approach_val = loaded_data[0], loaded_data[1], loaded_data[2], loaded_data[3], loaded_data[4], loaded_data[5] | |
gender_val, generation_val, psychotype_val, business_stage_val, industry_val, opf_val = loaded_data[6:12] | |
chosen_approach_val, p1, p2 = update_prompts_on_params_change(description_val, product_name_val, benefits_val, key_message_val, | |
gender_val, generation_val, psychotype_val, | |
business_stage_val, industry_val, opf_val) | |
return (selected_product_val, description_val, product_name_val, benefits_val, key_message_val, | |
gender_val, generation_val, psychotype_val, business_stage_val, industry_val, opf_val, | |
chosen_approach_val, p1, p2, "", "", "") | |
def save_preferred_sms_to_github( | |
selected_product, description, product_name, benefits, key_message, | |
gender, generation, psychotype, business_stage, industry, opf, | |
chosen_approach, presence_in_db, | |
model_1_name, prompt_1, final_prompt_1, sms_1, | |
model_2_name, prompt_2, final_prompt_2, sms_2, | |
chosen_sms | |
): | |
""" | |
Сохраняет выбранные поля в отдельный JSON-файл на GitHub, | |
чтобы не смешивать с предыдущими действиями, сохраняем под другим названием. | |
""" | |
# Собираем все данные | |
data_to_save = { | |
"timestamp": time.time(), | |
"product_dropdown": selected_product, | |
"description": description, | |
"product_name": product_name, | |
"benefits": benefits, | |
"key_message": key_message, | |
"gender": gender, | |
"generation": generation, | |
"psychotype": psychotype, | |
"business_stage": business_stage, | |
"industry": industry, | |
"opf": opf, | |
"chosen_approach": chosen_approach, | |
"comment": presence_in_db, | |
"model_1_name": model_1_name, | |
"prompt_1": prompt_1, | |
"final_prompt_1": final_prompt_1, | |
"sms_1": sms_1, | |
"model_2_name": model_2_name, | |
"prompt_2": prompt_2, | |
"final_prompt_2": final_prompt_2, | |
"sms_2": sms_2, | |
"preferred_sms": chosen_sms # "sms_1" или "sms_2" | |
} | |
file_content_encoded = base64.b64encode(json.dumps(data_to_save).encode()).decode() | |
filename = f"preferred_sms_{int(time.time())}.json" | |
url = f"https://api.github.com/repos/{repo}/contents/{filename}" | |
headers = { | |
"Authorization": f"token {token}", | |
"Content-Type": "application/json" | |
} | |
data = { | |
"message": f"Добавлен файл {filename} со сведениями о предпочтённом SMS", | |
"content": file_content_encoded | |
} | |
response = requests.put(url, headers=headers, data=json.dumps(data)) | |
if response.status_code == 201: | |
print(f"Файл {filename} успешно сохранен.") | |
else: | |
print(f"Ошибка при сохранении данных: {response.status_code}, {response.text}") | |
def on_prefer_sms_1( | |
selected_product, description, product_name, benefits, key_message, | |
gender, generation, psychotype, business_stage, industry, opf, | |
chosen_approach, presence_in_db, | |
model_1_name, prompt_1, final_prompt_1, sms_1, | |
model_2_name, prompt_2, final_prompt_2, sms_2 | |
): | |
""" | |
Вызывается при нажатии кнопки «Я предпочитаю это SMS» (для SMS 1). | |
""" | |
save_preferred_sms_to_github( | |
selected_product, description, product_name, benefits, key_message, | |
gender, generation, psychotype, business_stage, industry, opf, | |
chosen_approach, presence_in_db, | |
model_1_name, prompt_1, final_prompt_1, sms_1, | |
model_2_name, prompt_2, final_prompt_2, sms_2, | |
chosen_sms="sms_1" | |
) | |
return "Предпочтение SMS 1 сохранено в GitHub" | |
def on_prefer_sms_2( | |
selected_product, description, product_name, benefits, key_message, | |
gender, generation, psychotype, business_stage, industry, opf, | |
chosen_approach, presence_in_db, | |
model_1_name, prompt_1, final_prompt_1, sms_1, | |
model_2_name, prompt_2, final_prompt_2, sms_2 | |
): | |
""" | |
Вызывается при нажатии кнопки «Я предпочитаю это SMS» (для SMS 2). | |
""" | |
save_preferred_sms_to_github( | |
selected_product, description, product_name, benefits, key_message, | |
gender, generation, psychotype, business_stage, industry, opf, | |
chosen_approach, presence_in_db, | |
model_1_name, prompt_1, final_prompt_1, sms_1, | |
model_2_name, prompt_2, final_prompt_2, sms_2, | |
chosen_sms="sms_2" | |
) | |
return "Предпочтение SMS 2 сохранено в GitHub" | |
with gr.Blocks(theme="default") as demo: | |
gr.Markdown("**Процент созданных SMS по выбранному продукту**") | |
progress_bar_html = """ | |
<div style="width: 100%; background-color: #e0e0e0; border-radius: 10px; overflow: hidden;"> | |
<div style="width: 0%; background-color: #4caf50; height: 20px; text-align: center; color: white;"> | |
0% | |
</div> | |
</div> | |
""" | |
gr.HTML(progress_bar_html) | |
with gr.Row(): | |
with gr.Column(scale=1): | |
gr.Markdown("**Продукт**") | |
product_dropdown = gr.Dropdown(label="Продукт", choices=products, value=products[0]) | |
description = gr.Textbox(label="Описание предложения", lines=5, value="", interactive=True) | |
product_name = gr.Textbox(label="Наименование продукта", lines=1, value="", interactive=True) | |
benefits = gr.Textbox(label="Преимущества", lines=9, value="", interactive=True) | |
key_message = gr.Textbox(label="Ключевое сообщение", lines=2, value="") | |
def on_product_change(selected, description, product_name, benefits, key_message, | |
gender, generation, psychotype, business_stage, industry, opf): | |
if selected == "Свой продукт": | |
new_desc = "" | |
new_pname = "" | |
new_ben = "" | |
new_kmsg = "" | |
else: | |
if selected and selected in data_products["Наименование продукта"].values: | |
product_row = data_products[data_products["Наименование продукта"] == selected].iloc[0] | |
new_desc = product_row.get("Описание предложения", "") | |
new_pname = product_row.get("Наименование продукта", "") | |
new_ben = product_row.get("Преимущества", "") | |
new_kmsg = product_row.get("Ключевое сообщение", "") | |
else: | |
new_desc = "" | |
new_pname = "" | |
new_ben = "" | |
new_kmsg = "" | |
chosen_approach_val, p1, p2 = update_prompts_on_params_change(new_desc, new_pname, new_ben, new_kmsg, | |
gender, generation, psychotype, | |
business_stage, industry, opf) | |
return (gr.update(value=new_desc, interactive=(selected=="Свой продукт")), | |
gr.update(value=new_pname, interactive=(selected=="Свой продукт")), | |
gr.update(value=new_ben, interactive=(selected=="Свой продукт")), | |
gr.update(value=new_kmsg, interactive=(selected=="Свой продукт")), | |
chosen_approach_val, p1, p2, | |
"", "", "", "", "", "") | |
with gr.Column(scale=1): | |
gr.Markdown("**Клиент**") | |
gender_dropdown = gr.Dropdown(label="Пол", choices=["Не выбрано"]+genders, value=None) | |
generation_dropdown = gr.Dropdown(label="Поколение", choices=["Не выбрано"]+generations, value=None) | |
psychotype_dropdown = gr.Dropdown(label="Психотип", choices=["Не выбрано"]+psychotypes, value=None) | |
business_stage_dropdown = gr.Dropdown(label="Стадия бизнеса", choices=["Не выбрано"]+business_stages, value=None) | |
industry_dropdown = gr.Dropdown(label="Отрасль", choices=["Не выбрано"]+industries, value=None) | |
opf_dropdown = gr.Dropdown(label="ОПФ", choices=["Не выбрано"]+opfs, value=None) | |
chosen_approach = gr.Textbox(label="Выбранный подход", lines=1, value="", interactive=False) | |
presence_in_db = gr.Textbox(label="Комментарий", lines=1, value="", interactive=False) | |
with gr.Row(): | |
return_params_btn = gr.Button("Вернуть параметры предыдущего запроса") | |
set_unused_params_btn = gr.Button("Задать ранее невыставленные параметры (кнопка пока не работает)") | |
create_personal_sms_btn = gr.Button("Создать персонализированное SMS") | |
with gr.Row(): | |
with gr.Column(): | |
model_1_name = gr.Textbox(label="Модель 1", value="Grok-2-1212", interactive=False) | |
prompt_1 = gr.Textbox(label="Промпт 1", value="", interactive=False, lines=10) | |
sms_1 = gr.Textbox(label="SMS 1", lines=3, value="", interactive=False) | |
with gr.Column(): | |
model_2_name = gr.Textbox(label="Модель 2", value="Grok-2-1212", interactive=False) | |
prompt_2 = gr.Textbox(label="Промпт 2", value="", interactive=False, lines=10) | |
sms_2 = gr.Textbox(label="SMS 2", lines=3, value="", interactive=False) | |
with gr.Row(): | |
prefer_sms_1_btn = gr.Button("Я предпочитаю это SMS") | |
prefer_sms_2_btn = gr.Button("Я предпочитаю это SMS") | |
regen_btn = gr.Button("Перегенерировать SMS (не нравится ни одно из SMS)") | |
with gr.Row(): | |
comment_sms_1 = gr.Textbox(label="Комментарий к SMS 1", lines=2, value="") | |
comment_sms_2 = gr.Textbox(label="Комментарий к SMS 2", lines=2, value="") | |
with gr.Row(): | |
corrected_sms_1 = gr.Textbox(label="Откорректированное SMS 1", lines=3, value="") | |
corrected_sms_2 = gr.Textbox(label="Откорректированное SMS 2", lines=3, value="") | |
with gr.Row(): | |
save_sms_1_btn = gr.Button("Сохранить в базу (кнопка пока не работает)") | |
save_sms_2_btn = gr.Button("Сохранить в базу (кнопка пока не работает)") | |
final_prompt_1_state = gr.State("") | |
final_prompt_2_state = gr.State("") | |
product_dropdown.change( | |
fn=on_product_change, | |
inputs=[product_dropdown, description, product_name, benefits, key_message, | |
gender_dropdown, generation_dropdown, psychotype_dropdown, | |
business_stage_dropdown, industry_dropdown, opf_dropdown], | |
outputs=[description, product_name, benefits, key_message, | |
chosen_approach, prompt_1, prompt_2, | |
sms_1, sms_2, comment_sms_1, comment_sms_2, corrected_sms_1, corrected_sms_2] | |
) | |
def params_change_wrapper(description, product_name, benefits, key_message, | |
gender, generation, psychotype, business_stage, industry, opf): | |
chosen_approach_val, p1, p2 = update_prompts_on_params_change(description, product_name, benefits, key_message, | |
gender, generation, psychotype, | |
business_stage, industry, opf) | |
return chosen_approach_val, p1, p2, "", "", "", "", "", "" | |
client_params = [gender_dropdown, generation_dropdown, psychotype_dropdown, | |
business_stage_dropdown, industry_dropdown, opf_dropdown] | |
for cp in client_params: | |
cp.change( | |
fn=params_change_wrapper, | |
inputs=[description, product_name, benefits, key_message, | |
gender_dropdown, generation_dropdown, psychotype_dropdown, | |
business_stage_dropdown, industry_dropdown, opf_dropdown], | |
outputs=[chosen_approach, prompt_1, prompt_2, | |
sms_1, sms_2, comment_sms_1, comment_sms_2, corrected_sms_1, corrected_sms_2] | |
) | |
create_personal_sms_btn.click( | |
fn=generate_personalized_sms_wrapper, | |
inputs=[product_dropdown, description, product_name, benefits, key_message, | |
gender_dropdown, generation_dropdown, psychotype_dropdown, | |
business_stage_dropdown, industry_dropdown, opf_dropdown, | |
chosen_approach, prompt_1, prompt_2], | |
outputs=[sms_1, sms_2, final_prompt_1_state, final_prompt_2_state] | |
) | |
regen_btn.click( | |
fn=on_regenerate, | |
inputs=[ | |
product_dropdown, | |
description, | |
product_name, | |
benefits, | |
key_message, | |
gender_dropdown, | |
generation_dropdown, | |
psychotype_dropdown, | |
business_stage_dropdown, | |
industry_dropdown, | |
opf_dropdown, | |
chosen_approach, | |
presence_in_db, | |
model_1_name, | |
prompt_1, | |
final_prompt_1_state, | |
sms_1, | |
model_2_name, | |
prompt_2, | |
final_prompt_2_state, | |
sms_2 | |
], | |
outputs=[sms_1, sms_2] | |
) | |
return_params_btn.click( | |
fn=on_load_previous, | |
inputs=[], | |
outputs=[product_dropdown, description, product_name, benefits, key_message, | |
gender_dropdown, generation_dropdown, psychotype_dropdown, | |
business_stage_dropdown, industry_dropdown, opf_dropdown, | |
chosen_approach, prompt_1, prompt_2, | |
sms_1, sms_2, comment_sms_1, comment_sms_2, corrected_sms_1, corrected_sms_2] | |
) | |
prefer_sms_1_btn.click( | |
fn=on_prefer_sms_1, | |
inputs=[ | |
product_dropdown, | |
description, | |
product_name, | |
benefits, | |
key_message, | |
gender_dropdown, | |
generation_dropdown, | |
psychotype_dropdown, | |
business_stage_dropdown, | |
industry_dropdown, | |
opf_dropdown, | |
chosen_approach, | |
presence_in_db, | |
model_1_name, | |
prompt_1, | |
final_prompt_1_state, | |
sms_1, | |
model_2_name, | |
prompt_2, | |
final_prompt_2_state, | |
sms_2 | |
], | |
outputs=[] | |
) | |
prefer_sms_2_btn.click( | |
fn=on_prefer_sms_2, | |
inputs=[ | |
product_dropdown, | |
description, | |
product_name, | |
benefits, | |
key_message, | |
gender_dropdown, | |
generation_dropdown, | |
psychotype_dropdown, | |
business_stage_dropdown, | |
industry_dropdown, | |
opf_dropdown, | |
chosen_approach, | |
presence_in_db, | |
model_1_name, | |
prompt_1, | |
final_prompt_1_state, | |
sms_1, | |
model_2_name, | |
prompt_2, | |
final_prompt_2_state, | |
sms_2 | |
], | |
outputs=[] | |
) | |
demo.queue().launch() |