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 | |
import string | |
import math | |
import langchain_gigachat | |
import logging | |
from langchain.schema import SystemMessage | |
import pprint | |
from langchain_gigachat.chat_models import GigaChat | |
XAI_API_KEY = os.getenv("XAI_API_KEY") | |
client = OpenAI( | |
api_key=XAI_API_KEY, | |
base_url="https://api.x.ai/v1", | |
) | |
# Авторизация в GigaChat Pro | |
gc_key = os.getenv('GC_KEY') | |
chat_pro = GigaChat(credentials=gc_key, model='GigaChat-Max', max_tokens=68, temperature=0.87, verify_ssl_certs=False, scope="GIGACHAT_API_CORP") | |
# Функция для генерации сообщения с GigaChat Pro | |
def call_model(prompt): | |
try: | |
messages = [SystemMessage(content=prompt)] | |
res = chat_pro.invoke(messages) | |
print(res.content) | |
return res.content | |
except Exception as e: | |
return f"Ошибка при обращении к GigaChat-Pro: {e}" | |
token = os.getenv('GITHUB_TOKEN') | |
repo = "fruitpicker01/Storage_Anastasia" | |
current_request_index = -1 | |
ABSTRACT_PHRASES = [ | |
"отличный выбор", | |
"зарабатывай больше", | |
"отличная возможность", | |
"уникальная возможность", | |
"специальная скидка", | |
"без лишних формальностей", | |
"быстро и удобно в любое время", | |
"максимальная экономия", | |
"уверенный старт", | |
"простое управление", | |
] | |
CLICHE_PHRASES = [ | |
"источник гордости", | |
"откройте двери", | |
"мир бесконечных возможностей", | |
"ваш успех начинается здесь", | |
"максимальная выгода", | |
"гибкие условия", | |
"наша забота", | |
"что может быть проще", | |
"заслуживает лучшего", | |
"на полную мощность", | |
"не упустите свой шанс на развитие", | |
"не упусти возможность", | |
"не упусти свой шанс", | |
"спеши", | |
] | |
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, None | |
row = df[df[col] == param_value] | |
if row.empty: | |
return None, None | |
str_instr1 = row.iloc[0].get("Инструкция 1", "") | |
str_instr2 = row.iloc[0].get("Инструкция 2", "") | |
if not str_instr1.strip(): | |
str_instr1 = None | |
if not str_instr2.strip(): | |
str_instr2 = None | |
return str_instr1, str_instr2 | |
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, "ОПФ") | |
] | |
instructions_1_list = [] | |
instructions_2_list = [] | |
for (param_value, df, col) in instructions_data: | |
i1, i2 = get_instructions_for_param(param_value, df, col) | |
# i1, i2 — это то, что вернёт ваша новая функция | |
if i1: # если что-то вернулось из «Инструкция 1» | |
instructions_1_list.append(i1) | |
if i2: # если что-то вернулось из «Инструкция 2» | |
instructions_2_list.append(i2) | |
if not instructions_1_list: | |
return ("Для формирования промпта выберите хотя бы один личный персональный параметр для определения подхода", | |
"Для формирования промпта выберите хотя бы один личный персональный параметр для определения подхода") | |
lines_1 = [] | |
for i, instr_line in enumerate(instructions_1_list, start=1): | |
# какой-то формат | |
lines_1.append(f"{i}. {format_instruction_string(instr_line)}.") | |
mandatory_terms_1 = "\n".join(lines_1) | |
lines_2 = [] | |
for j, instr_line in enumerate(instructions_2_list, start=1): | |
lines_2.append(f"{j}. {format_instruction_string(instr_line)}.") | |
mandatory_terms_2 = "\n".join(lines_2) | |
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_1} | |
Убедись, что написал не меньше трех и не больше четырех предложений суммарной длиной от 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: | |
{mandatory_terms_2} | |
Убедись, что написал не меньше трех и не больше четырех предложений суммарной длиной от 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): | |
morph = pymorphy3.MorphAnalyzer() | |
text = re.sub(r'\s[-–—]\s', ' — ', text) | |
text = re.sub(r'(?<=\d)[-–—](?=\d)', '–', text) | |
text = re.sub(r'(?<=[a-zA-Zа-яА-Я0-9])[-–—](?=[a-zA-Zа-яА-Я0-9])', '-', text) | |
text = re.sub(r'"([^\"]+)"', r'«\1»', text) | |
text = re.sub(r'(\*|_|`)+', '', text) | |
if text.count('"') == 1: | |
text = text.replace('"', '') | |
if (text.startswith('"') and text.endswith('"')) or (text.startswith('«') and text.endswith('»')): | |
text = text[1:-1].strip() | |
text = re.sub(r'(\d+)[kкКK]', r'\1 000', text, flags=re.IGNORECASE) | |
greeting_patterns = [ | |
r"привет\b", r"здравствуй", r"добрый\s(день|вечер|утро)", | |
r"дорогой\b", r"уважаемый\b", r"дорогая\b", r"уважаемая\b", | |
r"господин\b", r"госпожа\b", r"друг\b", r"коллега\b", | |
r"товарищ\b", r"приятель\b", r"подруга\b" | |
] | |
def is_greeting_sentence(sentence): | |
words = sentence.split() | |
if len(words) < 5: | |
for word in words: | |
parsed = morph.parse(word.lower())[0] | |
for pattern in greeting_patterns: | |
if re.search(pattern, parsed.normal_form): | |
return True | |
return False | |
sentences = re.split(r'(?<=[.!?])\s+', text) | |
if sentences and is_greeting_sentence(sentences[0]): | |
sentences = sentences[1:] | |
text = ' '.join(sentences) | |
def restore_yo(text): | |
morph = pymorphy3.MorphAnalyzer() | |
words = text.split() | |
restored_words = [] | |
for word in words: | |
if word.isupper(): | |
restored_words.append(word) | |
continue | |
if word.lower() == "все": | |
restored_words.append(word) | |
continue | |
parsed = morph.parse(word)[0] | |
restored_word = parsed.word | |
if word and word[0].isupper(): | |
restored_word = restored_word.capitalize() | |
restored_words.append(restored_word) | |
return ' '.join(restored_words) | |
text = restore_yo(text) | |
text = re.sub(r'\bИп\b', 'ИП', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bОоо\b', 'ООО', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bРф\b', 'РФ', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bпользовуйтесь\b', 'пользуйтесь', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bею\b', 'ей', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bповышьте\b', 'повысьте', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bСбербизнес\b', 'СберБизнес', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bСбербизнеса\b', 'СберБизнес', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bСбербизнесе\b', 'СберБизнес', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bСбербанк\b', 'СберБанк', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bвашего ООО\b', 'вашей компании', text, flags=re.IGNORECASE) | |
text = re.sub(r'\b0₽\b', '0 р', text, flags=re.IGNORECASE) | |
text = re.sub(r'\b₽\b', 'р', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bруб\.(?=\W|$)', 'р', text, flags=re.IGNORECASE) | |
text = re.sub(r'\bруб(?:ля|лей)\b', 'р', text, flags=re.IGNORECASE) | |
text = re.sub(r'(\d+)\s+тысяч(?:а|и)?(?:\s+рублей)?', r'\1 000 р', text, flags=re.IGNORECASE) | |
text = re.sub(r'(\d+)\s*тыс\.\s*руб\.', r'\1 000 р', text, flags=re.IGNORECASE) | |
text = re.sub(r'(\d+)\s*тыс\.\s*р\.', r'\1 000 р', text, flags=re.IGNORECASE) | |
text = re.sub(r'(\d+)\s*тыс\.\s*р', r'\1 000 р', text, flags=re.IGNORECASE) | |
text = re.sub(r'(\d+)\s+миллиона\b|\bмиллионов\b', r'\1 млн', text, flags=re.IGNORECASE) | |
text = re.sub(r'(\d+)\s*млн\s*руб\.', r'\1 млн р', text, flags=re.IGNORECASE) | |
text = re.sub(r'(\d+)\s*р\b', r'\1 р', text) | |
text = re.sub(r'Qr', 'QR', text) | |
def remove_specific_sentences(text): | |
sentences = re.split(r'(?<=[.!?])\s+', text) | |
filtered_sentences = [ | |
sentence for sentence in sentences | |
if not re.search(r'\bникаких\s+(посещений|визитов)\b', sentence, re.IGNORECASE) | |
] | |
return ' '.join(filtered_sentences) | |
text = re.sub(r'\b(\d+)\s+000\s+000\s*р\b', r'\1 млн р', text, flags=re.IGNORECASE) | |
text = re.sub(r' р р ', r' р ', text, flags=re.IGNORECASE) | |
text = remove_specific_sentences(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] # «зарплатным» | |
others = [] | |
for i in range(len(product_words_original[1:])): # product_name без первого слова | |
# Если второе слово в продукте = «Карта», то вставляем ровно его | |
if product_words_original[i+1] == "Карта": | |
# Берём слово из оригинальной фразы | |
others.append(words[i+1]) | |
else: | |
# Смотрим, с какой буквы начинается? | |
# Если первая буква строчная, берём исходное (words[i+1]) | |
# Иначе берём из product_words_original | |
if words[i+1] and words[i+1][0].islower(): | |
others.append(words[i+1]) # Использовать форму, которую придумала модель | |
else: | |
others.append(product_words_original[i+1]) | |
# Остальные слова берем из оригинального названия продукта | |
replaced_words = [first_word] + others | |
# Собираем обратно измененную часть | |
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(model_prompt, product_name): | |
last_message = "" | |
msg = call_model(model_prompt) | |
msg = correct_dash_usage(msg) | |
msg = clean_message(msg) | |
msg = process_message(msg, product_name) | |
length = len(msg) | |
last_message = msg | |
length = len(msg) | |
msg += f"\n\n------\nКоличество знаков: {length}" | |
return msg | |
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() | |
# Фильтруем только user_request_*.json | |
json_files = [f for f in files if f['name'].startswith("user_request_") and f['name'].endswith(".json")] | |
if not json_files: | |
return (products[0], "", "", "", "", "", None, None, None, None, None, "", "", "", "", "", "") | |
# Сортируем по числу после user_request_ | |
# Разделяем на части user_request_XXXX.json -> берем XXXX как int | |
# Затем сортируем по этому числу | |
def extract_timestamp(name): | |
# name выглядит как "user_request_1735391048.json" | |
base = name.split('.')[0] # "user_request_1735391048" | |
part = base.split('_', maxsplit=1) # ["user_request", "1735391048"] | |
if len(part) == 2 and part[1].isdigit(): | |
return int(part[1]) | |
return 0 # fallback, если что-то не так | |
json_files.sort(key=lambda f: extract_timestamp(f['name']), reverse=True) | |
# Теперь самый новый (с максимальным числом) — json_files[0] | |
# «Предыдущий» в списке — json_files[1], и так далее. | |
# Уменьшаем current_request_index | |
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): | |
source_report, exceptions_dict = check_source_fields(description, product_name, benefits, key_message) | |
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_sms_with_timer(final_prompt_1, product_name, key_message) | |
# Выполняем проверки sms_1 | |
cut_sms_1 = cut_message(sms_1) | |
print("[DEBUG B] final check for SMS_1") | |
print("[DEBUG B] text going into perform_checks:", repr(cut_sms_1)) | |
print("[DEBUG B] key_message:", repr(key_message)) | |
print("[DEBUG B] exceptions_dict:", exceptions_dict) | |
print("[DEBUG B] exceptions_dict word_repetitions:", exceptions_dict.get("word_repetitions", None)) | |
checks_1 = perform_checks(cut_sms_1, key_message, exceptions_dict) | |
print("[DEBUG B] checks_1 =", checks_1) | |
checks_formatted_1 = format_checks(checks_1) | |
print("[DEBUG B] final checks_1 =>", checks_1) | |
print("[DEBUG B] final checks_formatted_1 =>", checks_formatted_1) | |
yield(sms_1, "", final_prompt_1, "", checks_formatted_1, "") | |
sms_2 = generate_sms_with_timer(final_prompt_2, product_name, key_message) | |
# Выполняем проверки sms_2 | |
cut_sms_2 = cut_message(sms_2) | |
print("[DEBUG B] final check for SMS_2") | |
print("[DEBUG B] text going into perform_checks:", repr(cut_sms_2)) | |
print("[DEBUG B] key_message:", repr(key_message)) | |
print("[DEBUG B] exceptions_dict:", exceptions_dict) | |
print("[DEBUG B] exceptions_dict word_repetitions:", exceptions_dict.get("word_repetitions", None)) | |
pprint.pprint(exceptions_dict, width=120) | |
checks_2 = perform_checks(cut_sms_2, key_message, exceptions_dict) | |
print("[DEBUG B] checks_2 =", checks_2) | |
checks_formatted_2 = format_checks(checks_2) | |
print("[DEBUG B] final checks_2 =>", checks_2) | |
print("[DEBUG B] final checks_formatted_2 =>", checks_formatted_2) | |
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) | |
yield(sms_1, sms_2, final_prompt_1, final_prompt_2, checks_formatted_1, checks_formatted_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 для обновления интерфейса. | |
""" | |
_, exceptions_dict = check_source_fields(description, product_name, benefits, key_message) | |
# Перегенерируем 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_sms_with_timer(final_prompt_1, product_name, key_message) | |
# Проверяем заново: | |
cut_sms_1 = cut_message(sms_1) | |
checks_1 = perform_checks(cut_sms_1, key_message, exceptions_dict) | |
print("[DEBUG on_regenerate] text for sms_1 final check:", repr(cut_sms_1)) | |
print("[DEBUG on_regenerate] key_message:", repr(key_message)) | |
print("[DEBUG on_regenerate] exceptions_dict word_repetitions:", exceptions_dict.get("word_repetitions", None)) | |
print("[DEBUG on_regenerate] checks_1 =", checks_1) | |
checks_formatted_1 = format_checks(checks_1) | |
yield sms_1, "", checks_formatted_1, "" | |
sms_2 = generate_sms_with_timer(final_prompt_2, product_name, key_message) | |
cut_sms_2 = cut_message(sms_2) | |
checks_2 = perform_checks(cut_sms_2, key_message, exceptions_dict) | |
print("[DEBUG on_regenerate] text for sms_2 final check:", repr(cut_sms_2)) | |
print("[DEBUG on_regenerate] key_message:", repr(key_message)) | |
print("[DEBUG on_regenerate] exceptions_dict word_repetitions:", exceptions_dict.get("word_repetitions", None)) | |
print("[DEBUG on_regenerate] checks_2 =", checks_2) | |
checks_formatted_2 = format_checks(checks_2) | |
# Теперь сохраняем всё, как при «Я предпочитаю это 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 не выбрано | |
) | |
print("[DEBUG B2] final checks_1 =>", checks_1) | |
print("[DEBUG B2] final checks_2 =>", checks_2) | |
yield sms_1, sms_2, checks_formatted_1, checks_formatted_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" | |
def save_sms_to_db( | |
selected_product, | |
description, | |
product_name, | |
benefits, | |
key_message, | |
gender, | |
generation, | |
psychotype, | |
business_stage, | |
industry, | |
opf, | |
chosen_approach, | |
presence_in_db, | |
model_name, | |
prompt_text, | |
final_prompt, # добавили финальный промпт | |
sms_text, | |
comment_sms, | |
corrected_sms | |
): | |
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": model_name, | |
"prompt": prompt_text, | |
"final_prompt": final_prompt, # сохраняем финальный промпт | |
"sms": sms_text, | |
"comment_sms": comment_sms, | |
"corrected_sms": corrected_sms | |
} | |
file_content_encoded = base64.b64encode(json.dumps(data_to_save).encode()).decode() | |
filename = f"saved_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 | |
} | |
resp = requests.put(url, headers=headers, data=json.dumps(data)) | |
if resp.status_code == 201: | |
print(f"Файл {filename} успешно сохранён (save_sms_to_db).") | |
else: | |
print(f"Ошибка при сохранении (save_sms_to_db): {resp.status_code}, {resp.text}") | |
def on_save_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, | |
comment_sms_1, | |
corrected_sms_1 | |
): | |
save_sms_to_db( | |
selected_product=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, | |
presence_in_db=presence_in_db, | |
model_name=model_1_name, | |
prompt_text=prompt_1, | |
final_prompt=final_prompt_1, | |
sms_text=sms_1, | |
comment_sms=comment_sms_1, | |
corrected_sms=corrected_sms_1 | |
) | |
return "SMS 1 сохранено в базу" | |
def on_save_sms_2( | |
selected_product, | |
description, | |
product_name, | |
benefits, | |
key_message, | |
gender, | |
generation, | |
psychotype, | |
business_stage, | |
industry, | |
opf, | |
chosen_approach, | |
presence_in_db, | |
model_2_name, | |
prompt_2, | |
final_prompt_2, | |
sms_2, | |
comment_sms_2, | |
corrected_sms_2 | |
): | |
save_sms_to_db( | |
selected_product=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, | |
presence_in_db=presence_in_db, | |
model_name=model_2_name, | |
prompt_text=prompt_2, | |
final_prompt=final_prompt_2, | |
sms_text=sms_2, | |
comment_sms=comment_sms_2, | |
corrected_sms=corrected_sms_2 | |
) | |
return "SMS 2 сохранено в базу" | |
def prepare_button_text(): | |
return gr.update(value="Сохраняется...", visible=True) | |
def update_button_text(): | |
return gr.update(value="Сохранено!", visible=True) | |
def reset_button_text(): | |
time.sleep(2) | |
return gr.update(value="Сохранить в базу", visible=True) | |
def reset_button_text_2(): | |
time.sleep(2) | |
return gr.update(value="Я предпочитаю это SMS", visible=True) | |
def check_source_fields(description, product_name, benefits, key_message): | |
results = [] | |
exceptions_dict = { | |
"forbidden_words": set(), | |
"greetings": set(), | |
"promises": set(), | |
"double_verbs": set(), | |
"participles": set(), | |
"adverbial_participles": set(), | |
"superlative_adjectives": set(), | |
"passive_voice": set(), | |
"written_out_ordinals": set(), | |
"repeating_conjunctions": set(), | |
"introductory_phrases": set(), | |
"amplifiers": set(), | |
"time_parasites": set(), | |
"multiple_nouns": set(), | |
"derived_prepositions": set(), | |
"compound_sentences": set(), | |
"dates_written_out": set(), | |
"word_repetitions": set() | |
} | |
# Проверяем "Описание предложения" | |
desc_checks = perform_checks(description, "") | |
# Удаляем length_check | |
desc_checks.pop("length_check", None) | |
not_passed_desc = extract_failed_checks(desc_checks, exceptions_dict, context="Описание предложения") | |
if not_passed_desc: | |
results.append(f"Описание предложения:\n{not_passed_desc}") | |
# Проверяем "Наименование продукта" | |
name_checks = perform_checks(product_name, "") | |
name_checks.pop("length_check", None) | |
not_passed_name = extract_failed_checks(name_checks, exceptions_dict, context="Наименование продукта") | |
if not_passed_name: | |
results.append(f"Наименование продукта:\n{not_passed_name}") | |
# Проверяем "Преимущества" | |
ben_checks = perform_checks(benefits, "") | |
ben_checks.pop("length_check", None) | |
not_passed_ben = extract_failed_checks(ben_checks, exceptions_dict, context="Преимущества") | |
if not_passed_ben: | |
results.append(f"Преимущества:\n{not_passed_ben}") | |
# Проверяем "Ключевое сообщение" | |
km_checks = perform_checks(key_message, "") | |
km_checks.pop("length_check", None) | |
not_passed_km = extract_failed_checks(km_checks, exceptions_dict, context="Ключевое сообщение") | |
if not_passed_km: | |
results.append(f"Ключевое сообщение:\n{not_passed_km}") | |
if not results: | |
return "Проверка исходных данных пройдена", exceptions_dict | |
else: | |
report = "\n\n".join(results) | |
return report, exceptions_dict | |
def on_check_source_fields(description, product_name, benefits, key_message): | |
report, exceptions_dict = check_source_fields(description, product_name, benefits, key_message) | |
return report | |
def extract_failed_checks(checks_dict, exceptions_dict, context=""): | |
""" | |
Пробегаемся по результатам checks_dict. | |
Если есть (False, reason), выводим reason, | |
и при необходимости парсим reason, чтобы добавить исключения в exceptions_dict. | |
""" | |
import re | |
import pymorphy3 | |
morph = pymorphy3.MorphAnalyzer() | |
lines = [] | |
def lemma_pair(word1, word2): | |
p1 = morph.parse(word1)[0].normal_form | |
p2 = morph.parse(word2)[0].normal_form | |
return (p1, p2) | |
for rule_key, result in checks_dict.items(): | |
if isinstance(result, tuple): | |
passed, reason = result | |
if not passed: | |
lines.append(f"{rule_to_str(rule_key)}: {reason}") | |
# Пример: если rule_key == "double_verbs" | |
if rule_key == "double_verbs": | |
# Ищем "...: 2 глагола подряд: позволяет зачислять" | |
# Допустим reason = "Не пройдена проверка на 2 глагола подряд: позволяет зачислять" | |
match = re.search(r'2 глагола подряд:\s*(\S+)\s+(\S+)$', reason) | |
if match: | |
w1 = match.group(1) | |
w2 = match.group(2) | |
pair_lemma = lemma_pair(w1, w2) | |
exceptions_dict.setdefault("double_verbs", set()).add(pair_lemma) | |
elif rule_key == "forbidden_words": | |
# reason вроде: "Запрещенное слово: продукт" | |
match = re.search(r'Запрещенное слово:\s*(\S+)', reason) | |
if match: | |
w = match.group(1) | |
l, _ = lemmatize_word(w, morph) | |
exceptions_dict.setdefault("forbidden_words", set()).add(l) | |
elif rule_key == "client_addressing": | |
# reason вроде: "Есть приветствие: дорогая" | |
match = re.search(r'приветствие:\s*(\S+)', reason, re.IGNORECASE) | |
if match: | |
w = match.group(1) | |
l, _ = lemmatize_word(w, morph) | |
exceptions_dict.setdefault("greetings", set()).add(l) | |
elif rule_key == "promises": | |
# reason напр. "Не пройдена проверка: обещания => обещать" | |
match = re.search(r'=>\s*(\S+)$', reason) | |
if match: | |
patt = match.group(1) | |
exceptions_dict.setdefault("promises", set()).add(patt) | |
elif rule_key == "participles": | |
# reason "Не пройдена проверка на причастие: повышающий" | |
match = re.search(r'причастие:\s*(\S+)$', reason) | |
if match: | |
w = match.group(1) | |
l, _ = lemmatize_word(w, morph) | |
exceptions_dict.setdefault("participles", set()).add(l) | |
elif rule_key == "adverbial_participles": | |
# reason: "деепричастие => рассматривая" | |
match = re.search(r'деепричастие\s*=>\s*(\S+)$', reason) | |
if match: | |
w = match.group(1) | |
l, _ = lemmatize_word(w, morph) | |
exceptions_dict.setdefault("adverbial_participles", set()).add(l) | |
elif rule_key == "superlative_adjectives": | |
# reason: "Не пройдена проверка на превосходную степень: сильнейший" | |
match = re.search(r'превосходную степень:\s*(\S+)$', reason) | |
if match: | |
w = match.group(1) | |
l, _ = lemmatize_word(w, morph) | |
exceptions_dict.setdefault("superlative_adjectives", set()).add(l) | |
elif rule_key == "passive_voice": | |
# reason: "Страдательный залог: построен" | |
match = re.search(r'страдательный залог:\s*(\S+)$', reason) | |
if match: | |
w = match.group(1) | |
l, _ = lemmatize_word(w, morph) | |
exceptions_dict.setdefault("passive_voice", set()).add(l) | |
elif rule_key == "written_out_ordinals": | |
# reason: "Порядковые числительные: десятый" | |
match = re.search(r'порядковые числительные:\s*(\S+)$', reason) | |
if match: | |
w = match.group(1) | |
exceptions_dict.setdefault("written_out_ordinals", set()).add(w) | |
elif rule_key == "repeating_conjunctions": | |
# reason: "Повторяющиеся союзы: ...", | |
match = re.search(r'союзы:\s*(\S+)', reason) | |
elif rule_key == "introductory_phrases": | |
match = re.search(r'конструкции:\s*(\S+)$', reason) | |
if match: | |
phrase = match.group(1).lower() | |
exceptions_dict.setdefault("introductory_phrases", set()).add(phrase) | |
elif rule_key == "amplifiers": | |
# reason: "Не пройдена проверка на усилители: очень" | |
match = re.search(r'усилители:\s*(\S+)$', reason) | |
if match: | |
w = match.group(1) | |
l, _ = lemmatize_word(w, morph) | |
exceptions_dict.setdefault("amplifiers", set()).add(l) | |
elif rule_key == "time_parasites": | |
# reason: "Не пройдена проверка на паразитов времени: срочно" | |
match = re.search(r'времени:\s*(\S+)$', reason) | |
if match: | |
w = match.group(1) | |
l, _ = lemmatize_word(w, morph) | |
exceptions_dict.setdefault("time_parasites", set()).add(l) | |
elif rule_key == "multiple_nouns": | |
# reason: "Несколько существительных подряд: ('зачисление','зарплата','сотрудникам')" | |
match = re.search(r'подряд:\s*(\([^)]+\))', reason) | |
if match: | |
chain_str = match.group(1) # "('зачисление','зарплата','сотрудникам')" | |
try: | |
chain_tuple = eval(chain_str) | |
exceptions_dict.setdefault("multiple_nouns", set()).add(chain_tuple) | |
except: | |
pass | |
elif rule_key == "derived_prepositions": | |
# reason: "Не пройдена проверка на производные предлоги: благодаря" | |
match = re.search(r'предлоги:\s*(\S+)$', reason) | |
if match: | |
w = match.group(1).lower() | |
exceptions_dict.setdefault("derived_prepositions", set()).add(w) | |
elif rule_key == "compound_sentences": | |
# reason может выглядеть так: | |
# "Не пройдена проверка: Сложноподчиненные предложения: как только" | |
# Разделим по двоеточию: | |
splitted = reason.split(': ', 2) | |
# splitted может быть ["Не пройдена проверка", "Сложноподчиненные предложения", "как только"] | |
if len(splitted) == 3: | |
raw_substring = splitted[2].strip() | |
# Добавляем сырую подстроку (без лемматизации) в исключения | |
exceptions_dict["compound_sentences"].add(raw_substring) | |
elif rule_key == "dates_written_out": | |
# reason: "Не пройдена проверка на даты прописью: пятнадцатого июля" | |
match = re.search(r'даты прописью:\s*(.+)$', reason) | |
if match: | |
full = match.group(1).strip() | |
splitted = full.split() | |
if len(splitted) == 2: | |
ord_str, month_str = splitted | |
l_o, _ = lemmatize_word(ord_str, morph) | |
l_m, _ = lemmatize_word(month_str, morph) | |
pair = (l_o, l_m) | |
exceptions_dict.setdefault("dates_written_out", set()).add(pair) | |
elif rule_key == "word_repetitions": | |
# reason: "Не пройдена проверка на повторы слов: зачисление" | |
match = re.search(r'повторы слов:\s*(\S+)', reason, re.IGNORECASE) | |
if match: | |
rep_w = match.group(1) | |
l, _ = lemmatize_word(rep_w, morph) | |
print(f"[DEBUG] Adding word repetition exception: rep_w={rep_w}, lemma={l}") | |
exceptions_dict.setdefault("word_repetitions", set()).add(l) | |
elif rule_key == "abstract_phrases": | |
# reason например: "Найдена абстрактная фраза: «отличный выбор»" | |
match = re.search(r'абстрактная фраза:\s*«([^»]+)»', reason, re.IGNORECASE) | |
if match: | |
found_phrase = match.group(1).lower() | |
exceptions_dict.setdefault("abstract_phrases", set()).add(found_phrase) | |
elif rule_key == "cliche": | |
# reason например: "Найдено клише: «на полную мощность»" | |
match = re.search(r'Найдено клише:\s*«([^»]+)»', reason, re.IGNORECASE) | |
if match: | |
found_phrase = match.group(1).lower() | |
exceptions_dict.setdefault("cliche", set()).add(found_phrase) | |
elif result is False: | |
# Нет причины | |
lines.append(f"{rule_to_str(rule_key)}: (без пояснения)") | |
return "\n".join(lines) | |
def rule_to_str(rule_key): | |
translation = { | |
"forbidden_words": "Запрещенные слова", | |
"client_addressing": "Обращение к клиенту", | |
"promises": "Обещания и гарантии", | |
"double_verbs": "Два глагола подряд", | |
"participles": "Причастия", | |
"adverbial_participles": "Деепричастия", | |
"superlative_adjectives": "Превосходная степень", | |
"passive_voice": "Страдательный залог", | |
"written_out_ordinals": "Порядковые числительные", | |
"subordinate_clauses_chain": "Цепочки придаточных", | |
"repeating_conjunctions": "Повторяющиеся союзы", | |
"introductory_phrases": "Вводные конструкции", | |
"amplifiers": "Усилители", | |
"time_parasites": "Паразиты времени", | |
"multiple_nouns": "Несколько существительных подряд", | |
"derived_prepositions": "Производные предлоги", | |
"compound_sentences": "Сложноподчиненные предложения", | |
"dates_written_out": "Даты прописью", | |
"word_repetitions": "Повторы слов", | |
"abstract_phrases": "Абстракции", | |
"cliche": "Клише" | |
} | |
return translation.get(rule_key, rule_key) | |
# ФУНКЦИИ ПРОВЕРОК (НАЧАЛО) | |
def lemmatize_word(word, morph): | |
""" | |
Возвращает (lemma, POS) для переданного слова. | |
""" | |
parsed = morph.parse(word) | |
if not parsed: | |
return word, None | |
best = parsed[0] | |
return best.normal_form, best.tag.POS | |
# 0. Проверка на длину | |
def check_length(message): | |
length = len(message) | |
if 160 <= length <= 250: | |
return True | |
else: | |
logging.warning(f"Не пройдена проверка: Длина сообщения {length} символов. Сообщение: {message}") | |
return False | |
# 1. Запрещенные слова | |
def check_forbidden_words(message, exceptions=None): | |
""" | |
Проверка на запрещённые слова. | |
Если лемма «запрещённого слова» находится в exceptions['forbidden_words'], | |
то пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_lemmas = exceptions.get("forbidden_words", set()) | |
morph = pymorphy3.MorphAnalyzer() | |
forbidden_patterns = [ | |
r'№\s?1\b', r'номер\sодин\b', r'номер\s1\b', | |
r'вкусный', r'дешёвый', r'продукт', | |
r'спам', r'банкротство', r'долг[и]?', r'займ', | |
r'срочный', r'главный', | |
r'гарантия', r'успех', r'лидер', 'никакой' | |
] | |
# Удаляем пунктуацию | |
message_no_punct = message.translate(str.maketrans('', '', string.punctuation)) | |
# Пример: «бессроч» => placeholder | |
placeholder = "заменабессроч" | |
message_no_punct = re.sub(r'\b\w*бессроч\w*\b', placeholder, message_no_punct, flags=re.IGNORECASE) | |
# Лемматизируем все слова | |
words = message_no_punct.split() | |
lemmas = [morph.parse(w)[0].normal_form for w in words] | |
lemmas = [re.sub(r'заменабессроч', 'бессроч', l) for l in lemmas] | |
normalized_msg = ' '.join(lemmas) | |
# Для каждого pattern проверяем, нет ли совпадения | |
for pattern in forbidden_patterns: | |
found = re.search(pattern, normalized_msg, re.IGNORECASE) | |
if found: | |
# Получим саму найденную строку | |
matched_str = found.group(0) | |
# Лемматизируем | |
lemma_found, _ = lemmatize_word(matched_str, morph) | |
if lemma_found not in allowed_lemmas: | |
return False, f"Запрещенное слово: {matched_str}" | |
return True | |
# 2 и #3. Обращение к клиенту и приветствие клиента | |
def check_no_greeting(message, exceptions=None): | |
""" | |
Проверка на «приветствия». | |
Если лемма слова среди exceptions['greetings'], пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_lemmas = exceptions.get("greetings", set()) | |
greeting_patterns = [ | |
r"привет\b", r"здравствуй", r"добрый\s(день|вечер|утро)", | |
r"дорогой\b", r"уважаемый\b", r"дорогая\b", r"уважаемая\b", | |
r"господин\b", r"госпожа\b", r"друг\b", r"коллега\b", | |
r"товарищ\b", r"приятель\b", r"подруга\b" | |
] | |
# Будем искать все совпадения паттернов | |
for pat in greeting_patterns: | |
match = re.search(pat, message, re.IGNORECASE) | |
if match: | |
found = match.group(0).lower() # «дорогая», «привет» и т.п. | |
morph = pymorphy3.MorphAnalyzer() | |
lemma, pos = lemmatize_word(found, morph) | |
if lemma not in allowed_lemmas: | |
return False, f"Есть приветствие: {found}" | |
return True | |
# 4. Обещания и гарантии | |
def check_no_promises(message, exceptions=None): | |
""" | |
Проверка на «обещания». | |
Если lemma слова в exceptions['promises'], то пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_lemmas = exceptions.get("promises", set()) | |
morph = pymorphy3.MorphAnalyzer() | |
patterns = ["обещать", "обещание", "гарантировать", "обязаться", "обязать", "обязательство", "обязательный"] | |
words = message.split() | |
lemmas = [morph.parse(w)[0].normal_form for w in words] | |
for patt in patterns: | |
if patt in lemmas: | |
if patt not in allowed_lemmas: | |
return False, f"Не пройдена проверка: обещания => {patt}" | |
return True | |
# 5. Составные конструкции из двух глаголов | |
def check_no_double_verbs(message, exceptions=None): | |
""" | |
Проверка на 2 подряд глагола. | |
Если (lemma1, lemma2) находится в exceptions['double_verbs'], то разрешаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_pairs = exceptions.get("double_verbs", set()) | |
morph = pymorphy3.MorphAnalyzer() | |
words = re.split(r'\s+|[.!?]', message) | |
tokens = [w.strip() for w in words if w.strip()] | |
parses = [morph.parse(tok)[0] for tok in tokens] | |
for i in range(len(parses) - 1): | |
if (parses[i].tag.POS in {'VERB', 'INFN'}) and (parses[i+1].tag.POS in {'VERB', 'INFN'}): | |
lemma1 = parses[i].normal_form | |
lemma2 = parses[i+1].normal_form | |
pair = (lemma1, lemma2) | |
# Если разрешено | |
if pair in allowed_pairs: | |
continue | |
# Если это "хотеть", "начинать", ... | |
if lemma1 in ["хотеть", "начинать", "начать"]: | |
continue | |
return False, f"Не пройдена проверка на 2 глагола подряд: {parses[i].word} {parses[i+1].word}" | |
return True | |
# 6. Причастия и причастные обороты | |
def check_no_participles(message, exceptions=None): | |
""" | |
Проверка на причастия. | |
Если lemma причастия в exceptions['participles'], разрешаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_lemmas = exceptions.get("participles", set()) | |
skip_lemmas = {"повысить", "увеличить", "понизить", "снизить"} | |
morph = pymorphy3.MorphAnalyzer() | |
words = message.split() | |
for w in words: | |
p = morph.parse(w)[0] | |
lemma = p.normal_form | |
if 'PRTF' in p.tag: | |
# Проверяем исключения | |
if lemma not in skip_lemmas and lemma not in allowed_lemmas: | |
return False, f"Не пройдена проверка на причастие: {p.word}" | |
return True | |
# 7. Деепричастия и деепричастные обороты | |
def check_no_adverbial_participles(message, exceptions=None): | |
""" | |
Проверка на деепричастия. | |
Если lemma в exceptions['adverbial_participles'], то не считаем нарушением. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_lemmas = exceptions.get("adverbial_participles", set()) | |
morph = pymorphy3.MorphAnalyzer() | |
words = message.split() | |
for w in words: | |
p = morph.parse(w)[0] | |
lemma = p.normal_form | |
if "GRND" in p.tag: | |
if lemma not in allowed_lemmas: | |
return False, f"Не пройдена проверка: деепричастие => {p.word}" | |
return True | |
# 8. Превосходная степень прилагательных | |
def check_no_superlative_adjectives(message, exceptions=None): | |
""" | |
Проверка на превосходную степень прилагательных. | |
Если lemma прилагательного среди exceptions['superlative_adjectives'], разрешаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_lemmas = exceptions.get("superlative_adjectives", set()) | |
morph = pymorphy3.MorphAnalyzer() | |
for w in message.split(): | |
p = morph.parse(w)[0] | |
lemma = p.normal_form | |
if 'Supr' in p.tag: | |
if lemma not in allowed_lemmas: | |
return False, f"Не пройдена проверка на превосходную степень: {p.word}" | |
return True | |
# 9. Страдательный залог | |
def check_no_passive_voice(message, exceptions=None): | |
""" | |
Проверка на страдательный залог. | |
Если lemma в exceptions['passive_voice'], пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_lemmas = exceptions.get("passive_voice", set()) | |
morph = pymorphy3.MorphAnalyzer() | |
words = re.findall(r'\b\w+(?:-\w+)*\b', message.lower()) | |
for w in words: | |
p = morph.parse(w)[0] | |
lemma = p.normal_form | |
if 'pssv' in p.tag: | |
if lemma not in allowed_lemmas: | |
return False, f"Не пройдена проверка на страдательный залог: {w}" | |
return True | |
# 10. Порядковые числительные от 10 прописью | |
def check_no_written_out_ordinals(message, exceptions=None): | |
""" | |
Проверка на порядковые числительные, написанные прописью (десятый и т.д.). | |
Если lemma в exceptions['written_out_ordinals'], пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_lemmas = exceptions.get("written_out_ordinals", set()) | |
morph = pymorphy3.MorphAnalyzer() | |
ordinal_words = [ | |
"десятый", "одиннадцатый", "двенадцатый", "тринадцатый", | |
"четырнадцатый", "пятнадцатый", "шестнадцатый", "семнадцатый", | |
"восемнадцатый", "девятнадцатый", "двадцатый" | |
] | |
tokens = message.split() | |
lemmas = [morph.parse(t)[0].normal_form for t in tokens] | |
for ow in ordinal_words: | |
if ow in lemmas: | |
if ow not in allowed_lemmas: | |
return False, f"Не пройдена проверка на порядковые числительные: {ow}" | |
return True | |
# 11. Цепочки с придаточными предложениями | |
def check_no_subordinate_clauses_chain(message): | |
# Регулярное выражение, которое ищет последовательности придаточных предложений | |
subordinate_clause_patterns = [ | |
r'\b(который|которая|которое|которые)\b', | |
r'\b(если|потому что|так как|что|когда)\b', | |
r'\b(хотя|несмотря на то что)\b' | |
] | |
# Разделяем сообщение на предложения по точке, вопросительному и восклицательному знакам | |
sentences = re.split(r'[.!?]\s*', message) | |
count = 0 | |
for sentence in sentences: | |
for pattern in subordinate_clause_patterns: | |
if re.search(pattern, sentence): | |
count += 1 | |
# Если в предложении найдено более одного придаточного предложения подряд, возвращаем False | |
if count < 2: | |
return True | |
else: | |
return False, f'Не пройдена проверка на цепочки с придаточными предложениями. Предложений: {count}' | |
# 12. Разделительные повторяющиеся союзы | |
def check_no_repeating_conjunctions(message, exceptions=None): | |
""" | |
Проверка на повторяющиеся союзы 'и', 'или' и т.п. | |
Если сам союз (в лемме) в exceptions['repeating_conjunctions'], пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_conjs = exceptions.get("repeating_conjunctions", set()) | |
pattern = re.compile(r'\b(и|ни|то|не то|или|либо)\b\s*(.*?)\s*,\s*\b\1\b', re.IGNORECASE) | |
sentences = re.split(r'[.!?]\s*', message) | |
for s in sentences: | |
m = pattern.search(s) | |
if m: | |
conj = m.group(1).lower() | |
if conj not in allowed_conjs: | |
return False, f"Не пройдена проверка на повторяющиеся союзы: {s}" | |
return True | |
# 13. Вводные конструкции | |
def check_no_introductory_phrases(message, exceptions=None): | |
""" | |
Проверка на вводные конструкции. | |
Если exact фраза в exceptions['introductory_phrases'], пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_phrases = exceptions.get("introductory_phrases", set()) | |
patterns = [ | |
r'\b(во-первых|во-вторых|с одной стороны|по сути|по правде говоря)\b', | |
r'\b(может быть|кстати|конечно|естественно|безусловно)\b' | |
] | |
for pat in patterns: | |
match = re.search(pat, message, re.IGNORECASE) | |
if match: | |
found = match.group(1).lower() | |
if found not in allowed_phrases: | |
return False, f"Не пройдена проверка на вводные конструкции: {found}" | |
return True | |
# 14. Усилители | |
def check_no_amplifiers(message, exceptions=None): | |
""" | |
Проверка на усилители (очень, крайне...). | |
Если лемма в exceptions['amplifiers'], пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_lemmas = exceptions.get("amplifiers", set()) | |
pattern = re.compile(r'\b(очень|крайне|чрезвычайно|совсем|полностью|чисто)\b', re.IGNORECASE) | |
matches = pattern.findall(message) | |
if matches: | |
morph = pymorphy3.MorphAnalyzer() | |
for m in matches: | |
lemma, _ = lemmatize_word(m, morph) | |
if lemma not in allowed_lemmas: | |
return False, f"Не пройдена проверка на усилители: {m}" | |
return True | |
# 15. Паразиты времени | |
def check_no_time_parasites(message, exceptions=None): | |
""" | |
Проверка на «паразиты времени» (немедленно, срочно...). | |
Если лемма в exceptions['time_parasites'], пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_lemmas = exceptions.get("time_parasites", set()) | |
pattern = re.compile(r'\b(немедленно|срочно|в данный момент)\b', re.IGNORECASE) | |
matches = pattern.findall(message) | |
if matches: | |
morph = pymorphy3.MorphAnalyzer() | |
for m in matches: | |
lemma, _ = lemmatize_word(m, morph) | |
if lemma not in allowed_lemmas: | |
return False, f"Не пройдена проверка на паразитов времени: {m}" | |
return True | |
# 16. Несколько существительных подряд | |
def check_no_multiple_nouns(message, exceptions=None): | |
""" | |
Проверка на 3+ подряд существительных в рамках одного предложения, | |
учитывая, что любой знак пунктуации тоже прерывает цепочку. | |
Если конкретная цепочка лемм не в exceptions['multiple_nouns'], считаем нарушением. | |
""" | |
import re | |
import pymorphy3 | |
if exceptions is None: | |
exceptions = {} | |
allowed_chains = exceptions.get("multiple_nouns", set()) | |
morph = pymorphy3.MorphAnalyzer() | |
# 1) Разбиваем весь текст на предложения по . ! ? или переводам строк | |
sentences = re.split(r'[.!?]\s*|\n+', message.strip()) | |
for sentence in sentences: | |
sentence = sentence.strip() | |
if not sentence: | |
continue | |
# 2) Внутри одного предложения извлекаем либо слово (\w+), либо "пунктуацию" ([^\w\s]+) | |
# \w+ = буквенно-цифровая последовательность | |
# [^\w\s]+ = "не-слово", "не-пробел" => любой набор знаков пунктуации | |
tokens = re.findall(r'\w+|[^\w\s]+', sentence) | |
chain = [] | |
count = 0 | |
for token in tokens: | |
# 3) Если это набор пунктуации, сбрасываем цепочку | |
if re.match(r'[^\w\s]+', token): | |
count = 0 | |
chain.clear() | |
continue | |
# Иначе это слово => проверяем, NOUN ли это | |
p = morph.parse(token)[0] | |
if 'NOUN' in p.tag: | |
count += 1 | |
chain.append(p.normal_form) | |
else: | |
count = 0 | |
chain.clear() | |
# 4) Если встретили 3+ подряд | |
if count > 2: | |
chain_tuple = tuple(chain) | |
if chain_tuple not in allowed_chains: | |
return False, f"Несколько существительных подряд: {chain_tuple}" | |
return True | |
# 17. Производные предлоги | |
def check_no_derived_prepositions(message, exceptions=None): | |
""" | |
Проверка на производные предлоги. | |
Если конкретный предлог в exceptions['derived_prepositions'], пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_preps = exceptions.get("derived_prepositions", set()) | |
pattern_text = (r'\b(в течение|в ходе|вследствие|в связи с|по мере|при помощи|' | |
r'согласно|вопреки|на основании|на случай|в продолжение|по причине|' | |
r'вблизи|вдалеке|вокруг|внутри|вдоль|посередине|вне|снаружи|' | |
r'благодаря|невзирая на|исходя из|благодаря)\b') | |
pat = re.compile(pattern_text, re.IGNORECASE) | |
matches = pat.findall(message) | |
if matches: | |
for m in matches: | |
low = m.lower() | |
if low not in allowed_preps: | |
return False, f"Не пройдена проверка на производные предлоги: {m}" | |
return True | |
# 19. Сложноподчиненные предложения | |
def check_no_compound_sentences(message, exceptions=None): | |
""" | |
Проверка на отсутствие сложноподчиненных предложений. | |
Если обнаружен союз/слово, которое есть в exceptions["compound_sentences"], | |
— НЕ считаем ошибкой. | |
Если нет в исключениях, считаем ошибкой. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
# Список союзов/фраз, по которым мы определяем сложноподчинённость | |
subordinating_conjunctions = [ | |
r'\bкогда\b', r'\bкак только\b', r'\bпока\b', r'\bпосле того как\b', | |
r'\bпотому что\b', r'\bтак как\b', r'\bоттого что\b', r'\bблагодаря тому что\b', | |
r'\bчтобы\b', r'\bдля того чтобы\b', r'\bесли\b', r'\bкогда бы\b', r'\bесли бы\b', | |
r'\bхотя\b', r'\bнесмотря на то что\b', r'\bбудто\b', r'\bсловно\b', r'\bкак будто\b' | |
] | |
# Собираем «разрешённые» фразы (из исключений) – это сырые строки: | |
allowed_raw_phrases = exceptions.get("compound_sentences", set()) | |
for pattern in subordinating_conjunctions: | |
# Находим все вхождения союзов | |
matches = re.finditer(pattern, message, re.IGNORECASE) | |
for m in matches: | |
raw_substring = m.group(0) # «сырая» найденная строка | |
# Если substring НЕ в наших исключениях: | |
# (сравниваем без учёта регистра — можно .lower() ) | |
if raw_substring.lower() not in (s.lower() for s in allowed_raw_phrases): | |
return (False, f'Не пройдена проверка: Сложноподчиненные предложения: {raw_substring}') | |
return True | |
# 20. Даты прописью | |
def check_no_dates_written_out(message, exceptions=None): | |
""" | |
Проверка на даты прописью. | |
Если (lemma_ordinal, lemma_month) в exceptions['dates_written_out'], пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_dates = exceptions.get("dates_written_out", set()) | |
morph = pymorphy3.MorphAnalyzer() | |
months = [ | |
"января", "февраля", "марта", "апреля", "мая", "июня", | |
"июля", "августа", "сентября", "октября", "ноября", "декабря" | |
] | |
date_patterns = [ | |
r'\b(первого|второго|третьего|четвертого|пятого|шестого|седьмого|' | |
r'восьмого|девятого|десятого|одиннадцатого|двенадцатого|' | |
r'тринадцатого|четырнадцатого|пятнадцатого|шестнадцатого|' | |
r'семнадцатого|восемнадцатого|девятнадцатого|двадцатого|' | |
r'двадцать первого|двадцать второго|двадцать третьего|' | |
r'двадцать четвертого|двадцать пятого|двадцать шестого|' | |
r'двадцать седьмого|двадцать восьмого|двадцать девятого|' | |
r'тридцатого|тридцать первого)\b' | |
] | |
for m in months: | |
for patt in date_patterns: | |
found = re.search(f"{patt}\\s{m}", message, re.IGNORECASE) | |
if found: | |
ordinal_str = found.group(1).lower() # например «пятнадцатого» | |
lemma_ord, _ = lemmatize_word(ordinal_str, morph) | |
lemma_month, _ = lemmatize_word(m, morph) | |
pair = (lemma_ord, lemma_month) # («пятнадцатый», «июль») | |
if pair not in allowed_dates: | |
return False, f"Не пройдена проверка на даты прописью: {found.group(0)}" | |
return True | |
# Проверка на абстракции | |
def check_abstract_phrases(message, exceptions=None): | |
""" | |
Критическая проверка на «абстрактные фразы» (ABSTRACT_PHRASES). | |
Если хоть одна из них найдена в тексте (регистронезависимо), | |
возвращаем (False, причина). | |
""" | |
if not exceptions: | |
exceptions = {} | |
allowed_phrases = exceptions.get("abstract_phrases", set()) | |
for phrase in ABSTRACT_PHRASES: | |
# если этой фразы нет в исключениях | |
if phrase.lower() not in allowed_phrases: | |
# проверяем, содержится ли она в сообщении | |
if phrase.lower() in message.lower(): | |
return (False, f"Найдена абстрактная фраза: «{phrase}»") | |
return True # если ничего не нашли | |
# Проверка на клише | |
def check_cliche(message, exceptions=None): | |
""" | |
Некритическая проверка на «клише» (CLICHE_PHRASES). | |
Аналогично, если находим — (False, причина). | |
""" | |
if not exceptions: | |
exceptions = {} | |
allowed_phrases = exceptions.get("cliche", set()) | |
for phrase in CLICHE_PHRASES: | |
if phrase.lower() not in allowed_phrases: | |
if phrase.lower() in message.lower(): | |
return (False, f"Найдено клише: «{phrase}»") | |
return True | |
# Доп правило. Повторы слов | |
def check_word_repetitions(message, key_message, exceptions=None): | |
""" | |
Проверка на повторы слов (кроме определённых частей речи). | |
Если lemma есть в exceptions['word_repetitions'], пропускаем. | |
""" | |
if exceptions is None: | |
exceptions = {} | |
allowed_lemmas = exceptions.get("word_repetitions", set()) | |
morph = pymorphy3.MorphAnalyzer() | |
ignore_pos = {'PREP', 'CONJ', 'PRON', 'INTJ', 'NUMR', 'PART', 'NPRO'} | |
msg_words = re.findall(r'\b\w+(?:-\w+)*\b', message.lower()) | |
# Ключевое сообщение | |
key_normalized = set() | |
for kw in re.findall(r'\b\w+\b', key_message.lower()): | |
lemma_k, pos_k = lemmatize_word(kw, morph) | |
key_normalized.add(lemma_k) | |
seen = {} | |
for w in msg_words: | |
lemma, pos = lemmatize_word(w, morph) | |
if (not pos) or (pos in ignore_pos): | |
continue | |
if lemma in key_normalized: | |
continue | |
if lemma in allowed_lemmas: | |
continue | |
if lemma in seen: | |
return False, f"Не пройдена проверка на повторы слов: {lemma}" | |
seen[lemma] = True | |
return True | |
# ФУНКЦИИ ПРОВЕРОК (КОНЕЦ) | |
CRITICAL_CHECKS = [ | |
"length_check", | |
"forbidden_words", | |
"client_addressing", | |
"promises", | |
"subordinate_clauses_chain", | |
"introductory_phrases", | |
"dates_written_out", | |
"abstract_phrases" | |
] | |
NON_CRITICAL_CHECKS = [ | |
"double_verbs", | |
"participles", | |
"adverbial_participles", | |
"superlative_adjectives", | |
"passive_voice", | |
"written_out_ordinals", | |
"repeating_conjunctions", | |
"amplifiers", | |
"time_parasites", | |
"multiple_nouns", | |
"derived_prepositions", | |
"compound_sentences", | |
"word_repetitions", | |
"cliche" | |
] | |
def run_checks_critical_and_non_critical(message: str, key_message: str) -> (bool, list): | |
""" | |
Возвращает (all_critical_passed: bool, failed_non_critical: list[str]) | |
где failed_non_critical = список названий проверок, которые не пройдены, но не являются критическими. | |
""" | |
checks = perform_checks(message, key_message) # ваша функция, которая возвращает dict | |
all_critical_passed = True | |
failed_non_critical = [] | |
for rule_name, result in checks.items(): | |
# result либо True/False, либо (False, reason) | |
if isinstance(result, tuple): | |
pass_flag = result[0] | |
else: | |
pass_flag = bool(result) | |
if rule_name in CRITICAL_CHECKS: | |
if not pass_flag: # Критическая не пройдена | |
all_critical_passed = False | |
elif rule_name in NON_CRITICAL_CHECKS: | |
if not pass_flag: | |
failed_non_critical.append(rule_name) | |
else: | |
# Все проверки, которых нет ни в CRITICAL_CHECKS, ни в NON_CRITICAL_CHECKS — игнорируем | |
pass | |
return all_critical_passed, failed_non_critical | |
def attempt_generate_sms_with_checks(model_prompt: str, product_name: str, key_message: str): | |
""" | |
Генерирует 1 SMS + запускает run_checks_critical_and_non_critical. | |
Возвращает (sms, all_critical_ok, failed_non_critical_list). | |
""" | |
sms = generate_message(model_prompt, product_name) | |
cut_sms = cut_message(sms) | |
print("[DEBUG CYCLE] about to run checks on iteration / generation") | |
print("[DEBUG CYCLE] text to check:", repr(cut_sms)) | |
print("[DEBUG CYCLE] key_message:", repr(key_message)) | |
all_critical_ok, failed_non_crit = run_checks_critical_and_non_critical(cut_sms, key_message) | |
print("[DEBUG A] attempt_generate_sms_with_checks => sms full:", repr(sms)) | |
print("[DEBUG A] attempt_generate_sms_with_checks => sms cut:", repr(cut_sms)) | |
print("[DEBUG A] attempt_generate_sms_with_checks => key_message:", repr(key_message)) | |
print("[DEBUG A] => all_critical_ok=", all_critical_ok, "failed_non_crit=", failed_non_crit) | |
return sms, all_critical_ok, failed_non_crit | |
def generate_sms_with_timer(model_prompt: str, product_name: str, key_message: str, max_time_sec=90): | |
""" | |
Псевдо-синхронный цикл с таймером 90 секунд. | |
Возвращает "оптимальный" SMS, | |
или строку "Не удалось за 1,5 минуты создать SMS, прошедшее все критические проверки". | |
""" | |
start = time.time() | |
best_sms = None | |
best_non_crit_count = math.inf # сколько некритич. проверок не пройдено (минимизируем) | |
i = 0 # Счётчик итераций | |
while True: | |
i += 1 | |
now = time.time() | |
elapsed = now - start | |
# Печатаем отладку по каждой итерации | |
print(f"[DEBUG] iteration={i}, elapsed={elapsed:.1f} s") | |
# Проверяем, не вышли ли за предел 90 секунд | |
if elapsed > max_time_sec: | |
print(f"[DEBUG] таймер вышел: elapsed={elapsed:.1f} s => break") | |
break | |
# Генерируем SMS и проверяем | |
sms, crit_ok, failed_non_crit = attempt_generate_sms_with_checks(model_prompt, product_name, key_message) | |
print(f"[DEBUG] iteration={i}, crit_ok={crit_ok}, failed_non_crit={failed_non_crit}") | |
if crit_ok: | |
# SMS прошло критические проверки | |
non_crit_count = len(failed_non_crit) | |
if non_crit_count == 0: | |
# Идеальное SMS => сразу return | |
print(f"[DEBUG] iteration={i} => нашли SMS без некритических ошибок, возвращаем сразу.") | |
return sms | |
if non_crit_count < best_non_crit_count: | |
best_non_crit_count = non_crit_count | |
best_sms = sms | |
print(f"[DEBUG] iteration={i} => новое лучшее SMS, некритических={best_non_crit_count}") | |
# Если crit_fail, идём на следующую итерацию, без обновлений best_sms | |
# (опционально) time.sleep(1) — чтобы не «спамить» модель слишком часто | |
# Если дошли сюда, значит время вышло (или цикл прерван вручную где-то) | |
if best_sms is not None: | |
print(f"[DEBUG] время истекло, возвращаем best_sms c {best_non_crit_count} некрит. ошибками") | |
return best_sms | |
else: | |
print("[DEBUG] ни одно SMS не прошло критические проверки => возвращаем фейл") | |
return "Не удалось за 1,5 минуты создать SMS, прошедшее все критические проверки." | |
def cut_message(message: str): | |
if '------' in message: | |
message = message.split('------')[0].strip() | |
return message | |
def safe_check(func, message, key_message=None): | |
try: | |
import inspect | |
sig = inspect.signature(func) | |
if len(sig.parameters) == 2: | |
return func(message, key_message) | |
else: | |
return func(message) | |
except Exception as e: | |
import traceback | |
print(f"[ERROR in {func.__name__}]") | |
traceback.print_exc() # выведет traceback | |
return None | |
def perform_checks(message, key_message, exceptions_dict=None): | |
""" | |
Запускает все проверки для данного message. | |
exceptions_dict - словарь исключений: | |
{ | |
"compound_sentences": set(...), | |
"double_verbs": set(...), | |
... | |
} | |
и т.д. | |
""" | |
checks = { | |
"length_check": safe_check(check_length, message), | |
"forbidden_words": safe_check(lambda msg, km: check_forbidden_words(msg, exceptions_dict), message), | |
"client_addressing": safe_check(lambda msg, km: check_no_greeting(msg, exceptions_dict), message), | |
"promises": safe_check(lambda msg, km: check_no_promises(msg, exceptions_dict), message), | |
"double_verbs": safe_check(lambda msg, km: check_no_double_verbs(msg, exceptions_dict), message), | |
"participles": safe_check(lambda msg, km: check_no_participles(msg, exceptions_dict), message), | |
"adverbial_participles": safe_check(lambda msg, km: check_no_adverbial_participles(msg, exceptions_dict), message), | |
"superlative_adjectives": safe_check(lambda msg, km: check_no_superlative_adjectives(msg, exceptions_dict), message), | |
"passive_voice": safe_check(lambda msg, km: check_no_passive_voice(msg, exceptions_dict), message), | |
"written_out_ordinals": safe_check(lambda msg, km: check_no_written_out_ordinals(msg, exceptions_dict), message), | |
"subordinate_clauses_chain": safe_check(check_no_subordinate_clauses_chain, message), | |
"repeating_conjunctions": safe_check(lambda msg, km: check_no_repeating_conjunctions(msg, exceptions_dict), message), | |
"introductory_phrases": safe_check(lambda msg, km: check_no_introductory_phrases(msg, exceptions_dict), message), | |
"amplifiers": safe_check(lambda msg, km: check_no_amplifiers(msg, exceptions_dict), message), | |
"time_parasites": safe_check(lambda msg, km: check_no_time_parasites(msg, exceptions_dict), message), | |
"multiple_nouns": safe_check(lambda msg, km: check_no_multiple_nouns(msg, exceptions_dict), message), | |
"derived_prepositions": safe_check(lambda msg, km: check_no_derived_prepositions(msg, exceptions_dict), message), | |
"compound_sentences": safe_check(lambda msg, km: check_no_compound_sentences(msg, exceptions_dict), message), | |
"dates_written_out": safe_check(lambda msg, km: check_no_dates_written_out(msg, exceptions_dict), message), | |
"word_repetitions": safe_check(lambda msg, km: check_word_repetitions(msg, km, exceptions_dict), message, key_message), | |
"abstract_phrases": safe_check(lambda msg: check_abstract_phrases(msg, exceptions_dict), message), | |
"cliche": safe_check(lambda msg: check_cliche(msg, exceptions_dict), message) | |
} | |
print(f"[DEBUG perform_checks] message={repr(message)} key_message={repr(key_message)}\n" | |
f" => checks={checks}\n" | |
f" => exceptions_dict={exceptions_dict}") | |
return checks | |
def format_checks(checks): | |
translation = { | |
"length_check": "Длина", | |
"forbidden_words": "Запрещенные слова", | |
"client_addressing": "Обращение к клиенту", | |
"promises": "Обещания и гарантии", | |
"double_verbs": "Два глагола подряд", | |
"participles": "Причастия", | |
"adverbial_participles": "Деепричастия", | |
"superlative_adjectives": "Превосходная степень", | |
"passive_voice": "Страдательный залог", | |
"written_out_ordinals": "Порядковые числительные", | |
"subordinate_clauses_chain": "Цепочки придаточных", | |
"repeating_conjunctions": "Повторяющиеся союзы", | |
"introductory_phrases": "Вводные конструкции", | |
"amplifiers": "Усилители", | |
"time_parasites": "Паразиты времени", | |
"multiple_nouns": "Несколько существительных подряд", | |
"derived_prepositions": "Производные предлоги", | |
"compound_sentences": "Сложноподчиненные предложения", | |
"dates_written_out": "Даты прописью", | |
"word_repetitions": "Повторы слов", | |
"abstract_phrases": "Абстракции", | |
"cliche": "Клише" | |
} | |
critical_lines = [] | |
non_critical_lines = [] | |
lines = [] | |
for rule, result in checks.items(): | |
rule_name = translation.get(rule, rule) # на случай, если нет в словаре | |
# Если результат — кортеж (False, "причина") | |
if isinstance(result, tuple): | |
passed, msg = result | |
if passed is True: | |
symbol = "✔️" | |
else: | |
symbol = "❌" | |
# Если результат — просто True/False | |
elif result is True: | |
symbol = "✔️" | |
elif result is False: | |
symbol = "❌" | |
# Если None или что-то иное — ставим вопрос | |
else: | |
symbol = "❓" | |
# Формируем строку вида "Имя проверки: ✔️/❌" | |
line = f"{rule_name}: {symbol}" | |
# Раскладываем по группам | |
if rule in CRITICAL_CHECKS: | |
critical_lines.append(line) | |
else: | |
non_critical_lines.append(line) | |
text_parts = [] | |
if critical_lines: | |
text_parts.append("**Критические проверки**:") | |
text_parts.extend(critical_lines) | |
if non_critical_lines: | |
text_parts.append("") | |
text_parts.append("**Некритические проверки**:") | |
text_parts.extend(non_critical_lines) | |
# Склеиваем в одну многострочную строку | |
result_text = " \n".join(text_parts) | |
return result_text | |
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=3, value="", interactive=True) | |
product_name = gr.Textbox(label="Наименование продукта", lines=1, value="", interactive=True) | |
benefits = gr.Textbox(label="Преимущества", lines=4, value="", interactive=True) | |
key_message = gr.Textbox(label="Ключевое сообщение", lines=1, value="") | |
check_source_btn = gr.Button("Проверить исходные данные") | |
source_check_md = gr.Textbox(label="Результат проверки исходных данных", lines=3, 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 | |
) | |
source_fields_report = check_source_fields(new_desc, new_pname, new_ben, new_kmsg) | |
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) | |
gr.Markdown("---") | |
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="GigaChat", 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="GigaChat", 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("Сохранить в базу") | |
with gr.Row(): | |
checks_sms_1 = gr.Markdown() | |
checks_sms_2 = gr.Markdown() | |
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, | |
source_check_md | |
] | |
) | |
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 | |
) | |
source_fields_report = check_source_fields(description, product_name, benefits, key_message) | |
return chosen_approach_val, p1, p2, "", "", "", "", "", "", source_fields_report | |
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=on_check_source_fields, | |
inputs=[description, product_name, benefits, key_message], | |
outputs=[source_check_md] | |
) | |
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, checks_sms_1, checks_sms_2] | |
) | |
check_source_btn.click( | |
fn=on_check_source_fields, | |
inputs=[description, product_name, benefits, key_message], | |
outputs=[source_check_md] | |
) | |
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, checks_sms_1, checks_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] | |
) | |
prefer_sms_1_btn.click( | |
fn=prepare_button_text, | |
inputs=[], | |
outputs=[prefer_sms_1_btn] | |
).then( | |
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=[] # или выводим что-то в текстбокс | |
).then( | |
fn=update_button_text, | |
inputs=[], | |
outputs=[prefer_sms_1_btn] | |
).then( | |
fn=reset_button_text_2, | |
inputs=[], | |
outputs=[prefer_sms_1_btn] | |
) | |
prefer_sms_2_btn.click( | |
fn=prepare_button_text, | |
inputs=[], | |
outputs=[prefer_sms_2_btn] | |
).then( | |
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=[] # или выводим что-то в текстбокс | |
).then( | |
fn=update_button_text, | |
inputs=[], | |
outputs=[prefer_sms_2_btn] | |
).then( | |
fn=reset_button_text_2, | |
inputs=[], | |
outputs=[prefer_sms_2_btn] | |
) | |
save_sms_1_btn.click( | |
fn=prepare_button_text, | |
inputs=[], | |
outputs=[save_sms_1_btn] | |
).then( | |
fn=on_save_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, | |
comment_sms_1, | |
corrected_sms_1 | |
], | |
outputs=[] | |
).then( | |
fn=update_button_text, | |
inputs=[], | |
outputs=[save_sms_1_btn] | |
).then( | |
fn=reset_button_text, | |
inputs=[], | |
outputs=[save_sms_1_btn] | |
) | |
save_sms_2_btn.click( | |
fn=prepare_button_text, | |
inputs=[], | |
outputs=[save_sms_2_btn] | |
).then( | |
fn=on_save_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_2_name, | |
prompt_2, | |
final_prompt_2_state, | |
sms_2, | |
comment_sms_2, | |
corrected_sms_2 | |
], | |
outputs=[] | |
).then( | |
fn=update_button_text, | |
inputs=[], | |
outputs=[save_sms_2_btn] | |
).then( | |
fn=reset_button_text, | |
inputs=[], | |
outputs=[save_sms_2_btn] | |
) | |
demo.queue().launch() |