fruitpicker01 commited on
Commit
981362f
1 Parent(s): 19f6073

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +131 -328
app.py CHANGED
@@ -18,9 +18,6 @@ import base64
18
  import io
19
  from transformers import AutoTokenizer, AutoModel
20
  from utils import best_text_choice
21
- import asyncio
22
- import inspect
23
- from together import AsyncTogether
24
 
25
  tokenizer = AutoTokenizer.from_pretrained("ai-forever/ru-en-RoSBERTa")
26
  model = AutoModel.from_pretrained("ai-forever/ru-en-RoSBERTa")
@@ -28,22 +25,13 @@ model = AutoModel.from_pretrained("ai-forever/ru-en-RoSBERTa")
28
  unique_sms_df = pd.read_parquet('unique_texts.parquet')
29
 
30
  MISTRAL_API_KEY = os.getenv('MISTRAL_API_KEY')
31
- MISTRAL_API_KEY_2 = os.getenv('MISTRAL_API_KEY_2')
32
- MISTRAL_API_KEY_3 = os.getenv('MISTRAL_API_KEY_3')
33
- MISTRAL_API_KEY_4 = os.getenv('MISTRAL_API_KEY_4')
34
- MISTRAL_API_KEY_5 = os.getenv('MISTRAL_API_KEY_5')
35
  token = os.getenv('GITHUB_TOKEN')
36
 
37
- async_client = AsyncTogether(api_key=os.environ.get("TOGETHER_API_KEY"))
38
-
39
  # Клиент для генерации сообщений
40
  client_mistral_generate = Mistral(api_key=MISTRAL_API_KEY)
41
 
42
  # Клиент для выполнения проверок
43
- client_mistral_check = Mistral(api_key=MISTRAL_API_KEY_2)
44
- client_mistral_check_2 = Mistral(api_key=MISTRAL_API_KEY_3)
45
- client_mistral_check_3 = Mistral(api_key=MISTRAL_API_KEY_4)
46
- client_mistral_check_4 = Mistral(api_key=MISTRAL_API_KEY_5)
47
 
48
  morph = pymorphy3.MorphAnalyzer()
49
 
@@ -200,8 +188,7 @@ def clean_message(message):
200
  # except Exception as e:
201
  # return f"Ошибка при обращении к GigaChat-Pro: {e}"
202
 
203
- async def generate_message_mistral_generate(prompt, max_retries=5):
204
-
205
  #def generate_message_mistral_generate(prompt):
206
  # try:
207
  # messages = [SystemMessage(content=prompt)]
@@ -214,173 +201,34 @@ async def generate_message_mistral_generate(prompt, max_retries=5):
214
  retries = 0
215
  while retries < max_retries:
216
  try:
217
- # Отправляем запрос к модели Qwen через Together API
218
- response = await async_client.chat.completions.create(
219
- model="Meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
220
- messages=[{"role": "user", "content": prompt}],
221
- temperature=0.9, # Параметры можно настроить по необходимости
222
- max_tokens=74
223
- )
224
-
225
- # Извлекаем и очищаем ответ
226
- cleaned_message = clean_message(response.choices[0].message.content.strip())
227
- return cleaned_message
228
-
229
- except Exception as e:
230
- error_message = str(e)
231
- # Проверяем, связана ли ошибка с превышением лимита или разрывом соединения
232
- if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
233
- wait_time = 3 # Время ожидания перед повторной попыткой
234
- print(f"Лимит запросов превышен или сервер не ответил. Ожидание {wait_time} секунд перед повторной попыткой...")
235
- await asyncio.sleep(wait_time)
236
- retries += 1
237
- else:
238
- # Для других типов ошибок выводим сообщение и прекращаем попытки
239
- print(f"Ошибка при вызове Together API: {e}")
240
- return None
241
-
242
- # Если все попытки исчерпаны, сообщаем об этом
243
- print("Не удалось получить ответ от Together API после максимального количества попыток.")
244
- return None
245
-
246
- # retries = 0
247
- # while retries < max_retries:
248
- # try:
249
- # chat_response = await client_mistral_generate.chat.complete_async(
250
- # model="open-mistral-nemo",
251
- # temperature=1.0,
252
- # min_tokens=71,
253
- # max_tokens=96,
254
- # messages=[
255
- # {
256
- # "role": "user",
257
- # "content": prompt
258
- # },
259
- # ]
260
- # )
261
- # cleaned_message = clean_message(chat_response.choices[0].message.content.strip())
262
- # return cleaned_message
263
- # except Exception as e:
264
- # error_message = str(e)
265
- # if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
266
- # wait_time = 3
267
- # print(f"Rate limit exceeded or server did not respond. Waiting {wait_time} seconds before retrying...")
268
- # await asyncio.sleep(wait_time)
269
- # retries += 1
270
- # else:
271
- # print(f"Error calling Mistral: {e}")
272
- # return None
273
- # print("Failed to get response from Mistral after maximum retries.")
274
- # return None
275
-
276
- async def generate_message_mistral_check(prompt, max_retries=5):
277
- #def generate_message_mistral_check(prompt):
278
- # try:
279
- # messages = [SystemMessage(content=prompt)]
280
- # res2 = chat_pro_check(messages)
281
- # cleaned_message = clean_message(res2.content.strip())
282
- # return cleaned_message
283
- # except Exception as e:
284
- # return f"Ошибка при обращении к GigaChat-Pro: {e}"
285
- retries = 0
286
- while retries < max_retries:
287
- try:
288
- # Отправляем запрос к модели Qwen через Together API
289
- response = await async_client.chat.completions.create(
290
- model="Meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
291
- messages=[{"role": "user", "content": prompt}],
292
  )
293
-
294
- # Извлекаем и очищаем ответ
295
- cleaned_message = clean_message(response.choices[0].message.content.strip())
296
  return cleaned_message
297
-
298
  except Exception as e:
299
  error_message = str(e)
300
- # Проверяем, связана ли ошибка с превышением лимита или разрывом соединения
301
  if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
302
- wait_time = 3 # Время ожидания перед повторной попыткой
303
- print(f"Лимит запросов превышен или сервер не ответил. Ожидание {wait_time} секунд перед повторной попыткой...")
304
- await asyncio.sleep(wait_time)
305
  retries += 1
306
  else:
307
- # Для других типов ошибок выводим сообщение и прекращаем попытки
308
- print(f"Ошибка при вызове Together API: {e}")
309
  return None
310
-
311
- # Если все попытки исчерпаны, сообщаем об этом
312
- print("Не удалось получить ответ от Together API после максимального количества попыток.")
313
  return None
314
 
315
- async def generate_message_mistral_check_2(prompt, max_retries=5):
316
- #def generate_message_mistral_check(prompt):
317
- # try:
318
- # messages = [SystemMessage(content=prompt)]
319
- # res2 = chat_pro_check(messages)
320
- # cleaned_message = clean_message(res2.content.strip())
321
- # return cleaned_message
322
- # except Exception as e:
323
- # return f"Ошибка при обращении к GigaChat-Pro: {e}"
324
-
325
- retries = 0
326
- while retries < max_retries:
327
- try:
328
- # Отправляем запрос к модели Qwen через Together API
329
- response = await async_client.chat.completions.create(
330
- model="Meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
331
- messages=[{"role": "user", "content": prompt}],
332
- )
333
-
334
- # Извлекаем и очищаем ответ
335
- cleaned_message = clean_message(response.choices[0].message.content.strip())
336
- return cleaned_message
337
-
338
- except Exception as e:
339
- error_message = str(e)
340
- # Проверяем, связана ли ошибка с превышением лимита или разрывом соединения
341
- if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
342
- wait_time = 3 # Время ожидания перед повторной попыткой
343
- print(f"Лимит запросов превышен или сервер не ответил. Ожидание {wait_time} секунд перед повторной попыткой...")
344
- await asyncio.sleep(wait_time)
345
- retries += 1
346
- else:
347
- # Для других типов ошибок выводим сообщение и прекращаем попытки
348
- print(f"Ошибка при вызове Together API: {e}")
349
- return None
350
-
351
- # Если все попытки исчерпаны, сообщаем об этом
352
- print("Не удалось получить ответ от Together API после максимального количества попыток.")
353
- return None
354
-
355
- # retries = 0
356
- # while retries < max_retries:
357
- # try:
358
- # chat_response = await client_mistral_check_2.chat.complete_async(
359
- # model="open-mistral-nemo",
360
- # temperature=0.2,
361
- # messages=[
362
- # {
363
- # "role": "user",
364
- # "content": prompt
365
- # },
366
- # ]
367
- # )
368
- # cleaned_message = clean_message(chat_response.choices[0].message.content.strip())
369
- # return cleaned_message
370
- # except Exception as e:
371
- # error_message = str(e)
372
- # if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
373
- # wait_time = 3
374
- # print(f"Rate limit exceeded or server did not respond. Waiting {wait_time} seconds before retrying...")
375
- # await asyncio.sleep(wait_time)
376
- # retries += 1
377
- # else:
378
- # print(f"Error calling Mistral: {e}")
379
- # return None
380
- # print("Failed to get response from Mistral after maximum retries.")
381
- # return None
382
-
383
- async def generate_message_mistral_check_3(prompt, max_retries=5):
384
  #def generate_message_mistral_check(prompt):
385
  # try:
386
  # messages = [SystemMessage(content=prompt)]
@@ -389,39 +237,32 @@ async def generate_message_mistral_check_3(prompt, max_retries=5):
389
  # return cleaned_message
390
  # except Exception as e:
391
  # return f"Ошибка при обращении к GigaChat-Pro: {e}"
392
-
393
  retries = 0
394
  while retries < max_retries:
395
  try:
396
- # Отправляем запрос к модели Qwen через Together API
397
- response = await async_client.chat.completions.create(
398
- model="Meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
399
- messages=[{"role": "user", "content": prompt}],
 
 
 
 
 
400
  )
401
-
402
- # Извлекаем и очищаем ответ
403
- cleaned_message = clean_message(response.choices[0].message.content.strip())
404
  return cleaned_message
405
-
406
  except Exception as e:
407
- error_message = str(e)
408
- # Проверяем, связана ли ошибка с превышением лимита или разрывом соединения
409
- if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
410
- wait_time = 3 # Время ожидания перед повторной попыткой
411
- print(f"Лимит запросов превышен или сервер не ответил. Ожидание {wait_time} секунд перед повторной попыткой...")
412
- await asyncio.sleep(wait_time)
413
- retries += 1
414
- else:
415
- # Для других типов ошибок выводим сообщение и прекращаем попытки
416
- print(f"Ошибка при вызове Together API: {e}")
417
- return None
418
-
419
- # Если все попытки исчерпаны, сообщаем об этом
420
- print("Не удалось получить ответ от Together API после максимального количества попыток.")
421
- return None
422
-
423
- async def generate_message_mistral_check_4(prompt, max_retries=5):
424
- #def generate_message_mistral_check(prompt):
425
  # try:
426
  # messages = [SystemMessage(content=prompt)]
427
  # res2 = chat_pro_check(messages)
@@ -429,37 +270,6 @@ async def generate_message_mistral_check_4(prompt, max_retries=5):
429
  # return cleaned_message
430
  # except Exception as e:
431
  # return f"Ошибка при обращении к GigaChat-Pro: {e}"
432
-
433
- retries = 0
434
- while retries < max_retries:
435
- try:
436
- # Отправляем запрос к модели Qwen через Together API
437
- response = await async_client.chat.completions.create(
438
- model="Meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
439
- messages=[{"role": "user", "content": prompt}],
440
- )
441
-
442
- # Извлекаем и очищаем ответ
443
- cleaned_message = clean_message(response.choices[0].message.content.strip())
444
- return cleaned_message
445
-
446
- except Exception as e:
447
- error_message = str(e)
448
- # Проверяем, связана ли ошибка с превышением лимита или разрывом соединения
449
- if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
450
- wait_time = 3 # Время ожидания перед повторной попыткой
451
- print(f"Лимит запросов превышен или сервер не ответил. Ожидание {wait_time} секунд перед повторной попыткой...")
452
- await asyncio.sleep(wait_time)
453
- retries += 1
454
- else:
455
- # Для других типов ошибок выводим сообщение и прекращаем попытки
456
- print(f"Ошибка при вызове Together API: {e}")
457
- return None
458
-
459
- # Если все попытки исчерпаны, сообщаем об этом
460
- print("Не удалось получить ответ от Together API после максимального количества попыток.")
461
- return None
462
-
463
 
464
  # Функция для замены сокращений с 'k' или 'К' на тысячи
465
  def replace_k_with_thousands(message):
@@ -628,7 +438,7 @@ def append_errors_to_prompt(prompt, checks):
628
  "derived_prepositions": "Не использовать производные предлоги.",
629
  "compound_sentences": "Избегать сложноподчиненных предложений.",
630
  "dates_written_out": "Не писать даты прописью.",
631
- # "no_word_repetitions": "НЕ ИСПОЛЬЗУЙ ПОВТОРЯЮЩИЕСЯ СЛОВА В СООБЩЕНИИ.",
632
  "disconnected_sentences": "Избегать сложных предложений без логической связи.",
633
  "synonymous_members": "Не использовать близкие по смыслу однородные члены предложения.",
634
  "clickbait_phrases": "Не использовать кликбейтные фразы.",
@@ -653,8 +463,8 @@ def append_errors_to_prompt(prompt, checks):
653
 
654
 
655
  def notify_failed_length(message_length):
656
- if message_length < 160:
657
- gr.Warning(f"Сообщение слишком короткое: {message_length} знаков. Минимум 160.")
658
  return False
659
  elif message_length > 250:
660
  gr.Warning(f"Сообщение слишком длинное: {message_length} знаков. Максимум 250.")
@@ -667,7 +477,7 @@ def notify_failed_checks(checks):
667
  "forbidden_words": "Запрещенные слова",
668
  "client_addressing": "Обращение к клиенту",
669
  "promises": "Обещания и гарантии",
670
- # "double_verbs": "Два глагола подряд",
671
  "participles": "Причастия",
672
  "adverbial_participles": "Деепричастия",
673
  "superlative_adjectives": "Превосходная степень",
@@ -682,7 +492,7 @@ def notify_failed_checks(checks):
682
  "derived_prepositions": "Производные предлоги",
683
  "compound_sentences": "Сложноподчиненные предложения",
684
  "dates_written_out": "Даты прописью",
685
- # "no_word_repetitions": "Повторы слов",
686
  "disconnected_sentences": "Сложные предложения без логической связи",
687
  "synonymous_members": "Близкие по смыслу однородные члены предложения",
688
  "clickbait_phrases": "Кликбейтные фразы",
@@ -735,24 +545,24 @@ def notify_failed_checks(checks):
735
  # return last_message
736
 
737
 
738
- async def generate_message_mistral_with_retry(prompt, approach_name, description, key_message, product_name, benefits):
739
  global approach_stats
740
  last_message = None
741
  for attempt in range(20):
742
- gr.Info(f"Итерация {attempt + 1}: генерация сообщения...")
743
- message = await generate_message_mistral_generate(prompt)
744
  if message is None:
745
- print("Failed to get message from Mistral, retrying...")
746
- # await asyncio.sleep(1)
747
  continue
748
  message = replace_k_with_thousands(message)
749
  message = correct_dash_usage(message)
750
  message_length = len(message)
751
  if not notify_failed_length(message_length):
752
  last_message = message
753
- # await asyncio.sleep(1)
754
  continue
755
- checks = await perform_checks(message, description, key_message, product_name, benefits)
756
  last_message = message
757
  # Инициализируем статистику для подхода, если ее нет
758
  if approach_name not in approach_stats:
@@ -766,7 +576,7 @@ async def generate_message_mistral_with_retry(prompt, approach_name, description
766
  if all(checks.values()):
767
  return message
768
  prompt = append_errors_to_prompt(prompt, checks)
769
- # await asyncio.sleep(1)
770
  gr.Info("Не удалось сгенерировать сообщение, соответствующее требованиям, за 20 итераций. Возвращаем последнее сгенерированное сообщение.")
771
  return last_message
772
 
@@ -779,7 +589,7 @@ def generate_standard_prompt(description, benefits, key_message, *selected_value
779
  f"Преимущества: {benefits}\n"
780
  "В тексте смс запрещено использование:\n"
781
  "- Запрещенные слова: № один, номер один, № 1, вкусный, дешёвый, продукт, спам, доступный, банкротство, долги, займ, срочно, сейчас, лучший, главный, номер 1, гарантия, успех, лидер, никакой;\n"
782
- "- ОДИНАКОВЫЕ СЛОВА В СООБЩЕНИИ;\n"
783
  "- Обращение к клиенту;\n"
784
  "- Приветствие клиента;\n"
785
  "- Обещания и гарантии;\n"
@@ -806,7 +616,7 @@ def generate_standard_prompt(description, benefits, key_message, *selected_value
806
  "- Узкоспециализированные термины;\n"
807
  "- Фразы, способные создать двойственное ощущение, обидеть;\n"
808
  "- Речевые клише, рекламные штампы, канцеляризмы;\n"
809
- "Убедись, что в готовом тексте до 250, но не менее 160 знаков с пробелами. Убедись, что в готовом тексте не менее трех предложений.\n"
810
  )
811
  if key_message.strip():
812
  prompt += f"Убедись, что в готовом тексте есть следующая ключевая информация: {key_message.strip()}"
@@ -818,7 +628,7 @@ def generate_standard_prompt(description, benefits, key_message, *selected_value
818
  def generate_personalization_prompt(key_message, *selected_values, prefix, suffix, product_name):
819
  prompt = f"{prefix}\n"
820
  prompt += f"Не изменяй название продукта: {product_name}.\n"
821
- prompt += "Адаптируй, не превышая длину сообщения в 250 знаков с пробелами (но и не менее 160 знаков с пробелами), текст с учетом следующих особенностей:\n"
822
  gender, generation, psychotype = selected_values[0], selected_values[1], selected_values[2]
823
  combined_instruction = ""
824
  additional_instructions = ""
@@ -882,7 +692,7 @@ def clean_prompt_for_display(prompt, prefixes, suffixes):
882
  return cleaned_prompt.strip()
883
 
884
  # Функция для постепенной генерации всех сообщений через yield
885
- async def generate_all_messages(desc, benefits, key_message, gender, generation, psychotype, business_stage, industry, opf, product_name):
886
  standard_prompt = generate_standard_prompt(desc, benefits, key_message)
887
  standard_prompt_for_display = f"Не изменяй название продукта: {product_name}.\n{standard_prompt}\nУбедись, что в готовом тексте без изменений, синонимов и перестановок слов используется наименование продукта: {product_name}.\n"
888
  approach_mapping = {
@@ -954,7 +764,7 @@ async def generate_all_messages(desc, benefits, key_message, gender, generation,
954
  yield selected_approaches_text_content, standard_prompt_for_display, display_personalization_prompt, None, None
955
  flag += 1
956
  prompt = add_prefix_suffix(standard_prompt, current_prefix, current_suffix, product_name)
957
- non_personalized_message = await generate_message_mistral_with_retry(prompt, approach_name, desc, key_message, product_name, benefits)
958
  non_personalized_length = len(non_personalized_message)
959
  non_personalized_display = f"{non_personalized_message}\n------\nКоличество знаков: {non_personalized_length}"
960
  if non_personalized_messages:
@@ -966,7 +776,7 @@ async def generate_all_messages(desc, benefits, key_message, gender, generation,
966
  non_personalized_messages, personalized_messages
967
  )
968
  full_personalized_prompt = f"{personalization_prompt}\n\nТекст для адаптации: {non_personalized_message}"
969
- personalized_message = await generate_message_mistral_with_retry(full_personalized_prompt, approach_name, desc, key_message, product_name, benefits)
970
  personalized_length = len(personalized_message)
971
  personalized_display = f"{personalized_message}\n------\nКоличество знаков: {personalized_length}"
972
  if personalized_messages:
@@ -977,7 +787,7 @@ async def generate_all_messages(desc, benefits, key_message, gender, generation,
977
  selected_approaches_text_content, standard_prompt_for_display, display_personalization_prompt,
978
  non_personalized_messages, personalized_messages
979
  )
980
- # time.sleep(1)
981
  save_statistics_to_github(approach_stats)
982
 
983
  def rank_messages(non_personalized_messages, personalized_messages):
@@ -1371,7 +1181,7 @@ def check_no_word_repetitions(message):
1371
  'и', 'а', 'но', 'или', 'да', 'ни', 'как', 'так',
1372
  'в', 'на', 'под', 'над', 'за', 'к', 'до', 'по', 'из', 'у', 'о', 'про', 'для',
1373
  'не', 'вот', 'это', 'тот', 'тем', 'при', 'чем',
1374
- 'же', 'ли', 'бы', 'то', 'р', 'от',
1375
  ])
1376
 
1377
  # Разбиваем текст на слова, удаляя знаки препинания
@@ -1451,7 +1261,7 @@ def cut_message(message):
1451
  return message
1452
 
1453
  # 22. Проверка сложных предложений без логической связи
1454
- async def check_disconnected_sentences(message):
1455
  message_clean = cut_message(message)
1456
  print()
1457
  print("Проверка 22: Проверка сложных предложений без логической связи")
@@ -1466,8 +1276,8 @@ async def check_disconnected_sentences(message):
1466
  если таких предложений **нет**, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
1467
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь **только** в формате JSON с закрывающими кавычками и скобками.**'''
1468
 
1469
- response = await generate_message_mistral_check(prompt)
1470
- # await asyncio.sleep(3) # Задержка в 3 секунды между запросами
1471
  print("Mistral response:", response) # Выводим полный ответ модели
1472
  result = parse_json_response(response)
1473
  if result is not None:
@@ -1479,7 +1289,7 @@ async def check_disconnected_sentences(message):
1479
  return None
1480
 
1481
  # 23. Проверка на близкие по смыслу однородные члены
1482
- async def check_synonymous_members(message):
1483
  print()
1484
  print("Проверка 23: Проверка на близкие по смыслу однородные члены")
1485
  print()
@@ -1493,8 +1303,8 @@ async def check_synonymous_members(message):
1493
  если таких слов или выражений нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
1494
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1495
 
1496
- response = await generate_message_mistral_check_2(prompt)
1497
- # await asyncio.sleep(3)
1498
  print("Mistral response:", response)
1499
  result = parse_json_response(response)
1500
  if result is not None:
@@ -1507,7 +1317,7 @@ async def check_synonymous_members(message):
1507
 
1508
 
1509
  # 24. Проверка на шокирующие, экстравагантные или кликбейтные фразы
1510
- async def check_clickbait_phrases(message, description, benefits, key_message):
1511
  message_clean = cut_message(message)
1512
  print()
1513
  print()
@@ -1523,7 +1333,6 @@ async def check_clickbait_phrases(message, description, benefits, key_message):
1523
  3. Стандартные рекламные призывы к действию, такие как "купите сейчас" или "узнайте больше", не считаются кликбейтом, если они не преувеличивают преимущества или не используют явную манипуляцию эмоциями.
1524
  4. Не считай фразы, используемые в исходном описании продукта, кликбейтными. Исходное описание: "{description}".
1525
  5. Не считай фразы, используемые в преимуществах продукта, кликбейтными. Преимущества: "{benefits}".
1526
- 6. Не считай фразы, используемые в ключевом сообщении, кликбейтными. Ключевое сообщение: "{key_message}".
1527
  Пример ответа:
1528
  {{"decision": false, "explanation": "Текст нейтрален и не содержит кликбейтных фраз."}}
1529
 
@@ -1532,8 +1341,8 @@ async def check_clickbait_phrases(message, description, benefits, key_message):
1532
 
1533
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1534
 
1535
- response = await generate_message_mistral_check_4(prompt)
1536
- # await asyncio.sleep(3)
1537
  print("Mistral response:", response)
1538
  result = parse_json_response(response)
1539
  if result is not None:
@@ -1546,7 +1355,7 @@ async def check_clickbait_phrases(message, description, benefits, key_message):
1546
 
1547
 
1548
  # 25. Проверка на абстрактные заявления без поддержки фактами
1549
- async def check_abstract_claims(message, description, benefits, key_message):
1550
  print()
1551
  print("Проверка 25: Проверка на абстрактные заявления без поддержки фактами")
1552
  print()
@@ -1573,9 +1382,8 @@ async def check_abstract_claims(message, description, benefits, key_message):
1573
  - "Снизьте финансовую нагрузку"
1574
  4. Ищи общие фразы, которые не дают представления о конкретной пользе, такие как "лучшее решение", "высокое качество", "отличный сервис", если они не сопровождаются пояснением о том, почему это так.
1575
  5. Учитывай, что в рекламных сообщениях допустимы эмоциональные и обобщённые фразы, если они достаточно конкретны для понимания аудитории, однако они должны сопровождаться фактами или подробными примерами.
1576
- 6. Не считай фразы, используемые в исходном описании продукта, абстрактными. Исходное описание: "{description}".
1577
- 7. Не с��итай фразы, используемые в преимуществах продукта, абстрактными. Преимущества: "{benefits}".
1578
- 8. Не считай фразы, используемые в ключевом сообщении, абстрактными. Ключевое сообщение: "{key_message}".
1579
 
1580
  Пример ответа:
1581
  {{"decision": false, "explanation": "Текст не содержит абстрактные утверждения без конкретики."}}
@@ -1585,8 +1393,8 @@ async def check_abstract_claims(message, description, benefits, key_message):
1585
 
1586
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1587
 
1588
- response = await generate_message_mistral_check_2(prompt)
1589
- # await asyncio.sleep(3)
1590
  print("Mistral response:", response)
1591
  result = parse_json_response(response)
1592
  if result is not None:
@@ -1599,7 +1407,7 @@ async def check_abstract_claims(message, description, benefits, key_message):
1599
 
1600
 
1601
  # 26. Проверка на узкоспециализированные термины
1602
- async def check_specialized_terms(message):
1603
  print()
1604
  print("Проверка 26: Проверка на узкоспециализированные термины")
1605
  print()
@@ -1622,8 +1430,8 @@ async def check_specialized_terms(message):
1622
 
1623
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1624
 
1625
- response = await generate_message_mistral_check_2(prompt)
1626
- # await asyncio.sleep(3)
1627
  print("Mistral response:", response)
1628
  result = parse_json_response(response)
1629
  if result is not None:
@@ -1635,7 +1443,7 @@ async def check_specialized_terms(message):
1635
  return None
1636
 
1637
  # 27. Проверка на двусмысленные или обидные фразы
1638
- async def check_offensive_phrases(message):
1639
  print()
1640
  print("Проверка 27: Проверка на двусмысленные или обидные фразы")
1641
  print()
@@ -1651,8 +1459,8 @@ async def check_offensive_phrases(message):
1651
  если таких фраз нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
1652
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1653
 
1654
- response = await generate_message_mistral_check_4(prompt)
1655
- # await asyncio.sleep(3)
1656
  print("Mistral response:", response)
1657
  result = parse_json_response(response)
1658
  if result is not None:
@@ -1664,7 +1472,7 @@ async def check_offensive_phrases(message):
1664
  return None
1665
 
1666
  # 28. Проверка на речевые клише, рекламные штампы и канцеляризмы
1667
- async def check_cliches_and_bureaucratese(message, description, benefits, key_message):
1668
  print()
1669
  print("Проверка 28: Проверка на речевые клише, рекламные штампы и канцеляризмы")
1670
  print()
@@ -1677,9 +1485,8 @@ async def check_cliches_and_bureaucratese(message, description, benefits, key_me
1677
  - Информацию о ценах, скидках, акциях или условиях покупки (например, "при покупках от 100 000 рублей в месяц").
1678
  - Описания способов оформления или получения услуг (например, "оформление возможно онлайн или в офисе").
1679
  - Стандартные отраслевые термины и фразы, необходимые для понимания сообщения (например, "премиальная бизнес-карта", "Mastercard Preferred"), но **не** их использование в комбинации с общими словами, как например, "идеальное решение для вашего бизнеса".
1680
- - Не считай фразы, используемые в исходном описании продукта, как клише. Исходное описание: "{description}".
1681
- - Не считай фразы, используемые в преимуществах продукта, как клише. Преимущества: "{benefits}".
1682
- - Не считай фразы, используемые в ключевом сообщении, как клише. Ключевое сообщение: "{key_message}".
1683
  **Считай клише или канцеляризмами следующие типы выражений:**
1684
  - Избитые фразы, такие как:
1685
  - "Обеспечьте стабильность и развитие вашего бизнеса"
@@ -1697,8 +1504,8 @@ async def check_cliches_and_bureaucratese(message, description, benefits, key_me
1697
  если в тексте **есть** такие выражения, **верни только** JSON {{"decision": true, "explanation": "<пояснение>"}}.
1698
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1699
 
1700
- response = await generate_message_mistral_check_3(prompt)
1701
- # await asyncio.sleep(3)
1702
  print("Mistral response:", response)
1703
  result = parse_json_response(response)
1704
  if result is not None:
@@ -1710,26 +1517,24 @@ async def check_cliches_and_bureaucratese(message, description, benefits, key_me
1710
  return None
1711
 
1712
  # 29. Проверка на соответствие описанию предложения и ключевому сообщению
1713
- async def check_no_contradictions(message, description, benefits, key_message):
1714
  print()
1715
  print("Проверка 29: Проверка на отсутствие противоречий с описанием предложения")
1716
  print()
1717
  message_clean = cut_message(message)
1718
- prompt = f'''Проверь, не противоречит ли следующее сообщение описанию предложения, преимуществам и ключевому сообщению. Учти, что сообщение является выжимкой из описания предложения и не может содержать столько же информации в том же объеме, сколько описание предложения - важно, чтобы в сообщении не было указано ложных фактов.
1719
  Описание предложения:
1720
  "{description}"
1721
- Преимушества:
1722
- "{benefits}"
1723
  Ключевое сообщение:
1724
  "{key_message}"
1725
  Сообщение:
1726
  "{message}"
1727
- Если сообщение не содержит фактов, которые отсутствуют в описании предложения, преиуществах и ключевом сообщении, **верни только** JSON {{"decision": false, "explanation": "Противоречий не обнаружено."}}.
1728
- Если сообщение содержит факты, которые отсутствуют в описании предложения, преимуществах и ключевом сообщении, **верни только** JSON {{"decision": true, "explanation": "<описание противоречий>"}}.
1729
  **Не добавляй никакого дополнительного текста. Отвечай только в формате JSON с закрывающими кавычками и скобками.**'''
1730
 
1731
- response = await generate_message_mistral_check(prompt)
1732
- # await asyncio.sleep(3)
1733
  print("Mistral response:", response)
1734
  result = parse_json_response(response)
1735
  if result is not None:
@@ -1741,7 +1546,7 @@ async def check_no_contradictions(message, description, benefits, key_message):
1741
  return None
1742
 
1743
  # 30. Проверка на наличие ключевого сообщения
1744
- async def check_contains_key_message(message, key_message):
1745
  print()
1746
  print("Проверка 30: Проверка на наличие ключевого сообщения")
1747
  print()
@@ -1755,8 +1560,8 @@ async def check_contains_key_message(message, key_message):
1755
  Если сообщение **не содержит всю** информацию из ключевого текста, **верни только** JSON {{"decision": true, "explanation": "Ключевое текст отсутствует."}}.
1756
  **Не добавляй никакого дополнительного текста. Отвечай только в формате JSON с закрывающими кавычками и скобками.**'''
1757
 
1758
- response = await generate_message_mistral_check(prompt)
1759
- # await asyncio.sleep(3)
1760
  print("Mistral response:", response)
1761
  result = parse_json_response(response)
1762
  if result is not None:
@@ -1768,7 +1573,7 @@ async def check_contains_key_message(message, key_message):
1768
  return None
1769
 
1770
  # 31. Проверка на точное совпадение названий продуктов
1771
- async def check_product_name_consistency(message, product_name):
1772
  print()
1773
  print("Проверка 31: Проверка на точное совпадение названий продуктов")
1774
  print()
@@ -1785,8 +1590,8 @@ async def check_product_name_consistency(message, product_name):
1785
  Если название продукта **не** совпадает, **верни только** JSON {{"decision": true, "explanation": "<описание несоответствия>"}}.
1786
  **Не добавляй никакого дополнительного текста. Ответ должен быть только в формате JSON с закрывающими кавычками и скобками.**'''
1787
 
1788
- response = await generate_message_mistral_check_3(prompt)
1789
- # await asyncio.sleep(3)
1790
  print("Mistral response:", response)
1791
  result = parse_json_response(response)
1792
  if result is not None:
@@ -1800,63 +1605,61 @@ async def check_product_name_consistency(message, product_name):
1800
  # ФУНКЦИИ ПРОВЕРОК (КОНЕЦ)
1801
 
1802
 
1803
- async def safe_check(func, *args):
1804
  try:
1805
- if inspect.iscoroutinefunction(func):
1806
- return await func(*args)
1807
- else:
1808
- return func(*args)
1809
  except Exception as e:
1810
- print(f"Error in {func.__name__}: {e}")
1811
- return None
 
1812
 
1813
- async def perform_checks(message, description, key_message, product_name, benefits):
1814
  checks = {}
1815
 
1816
  # 2. Morphological checks using pymorphy3
1817
  morphological_checks = [
1818
- # ("no_word_repetitions", check_no_word_repetitions),
1819
  ("forbidden_words", check_forbidden_words),
1820
- # ("double_verbs", check_no_double_verbs),
1821
- ("adverbial_participles", check_no_adverbial_participles),
1822
- ("derived_prepositions", check_no_derived_prepositions),
1823
- ("multiple_nouns", check_no_multiple_nouns),
1824
- ("introductory_phrases", check_no_introductory_phrases),
1825
- ("compound_sentences", check_no_compound_sentences),
1826
- ("superlative_adjectives", check_no_superlative_adjectives),
1827
  ("client_addressing", check_no_greeting),
1828
  ("promises", check_no_promises),
1829
- ("amplifiers", check_no_amplifiers),
1830
  ("participles", check_no_participles),
 
 
1831
  ("passive_voice", check_no_passive_voice),
1832
  ("written_out_ordinals", check_no_written_out_ordinals),
1833
  ("subordinate_clauses_chain", check_no_subordinate_clauses_chain),
1834
  ("repeating_conjunctions", check_no_repeating_conjunctions),
 
 
1835
  ("time_parasites", check_no_time_parasites),
 
 
 
1836
  ("dates_written_out", check_no_dates_written_out),
 
1837
  ]
1838
 
1839
  # 3. LLM checks: check_clickbait_phrases, check_abstract_claims, check_cliches_and_bureaucratese
1840
  llm_checks_group1 = [
 
1841
  ("no_contradictions", check_no_contradictions),
 
1842
  ("abstract_claims", check_abstract_claims),
1843
  ("cliches_and_bureaucratese", check_cliches_and_bureaucratese),
1844
- ("clickbait_phrases", check_clickbait_phrases),
1845
- ("contains_key_message", check_contains_key_message),
1846
- ("synonymous_members", check_synonymous_members),
1847
- ("product_name_consistency", check_product_name_consistency)
1848
  ]
1849
 
1850
  # 4. Remaining LLM checks
1851
  llm_checks_group2 = [
1852
- ("offensive_phrases", check_offensive_phrases),
1853
  ("disconnected_sentences", check_disconnected_sentences),
1854
- ("specialized_terms", check_specialized_terms)
 
 
1855
  ]
1856
 
1857
  # Perform morphological checks
1858
  for check_name, check_func in morphological_checks:
1859
- result = await safe_check(check_func, message)
1860
  checks[check_name] = result
1861
  if result is False:
1862
  return checks # Stop on first failure
@@ -1864,19 +1667,19 @@ async def perform_checks(message, description, key_message, product_name, benefi
1864
  # Perform LLM checks group 1
1865
  for check_name, check_func in llm_checks_group1:
1866
  if check_name == "no_contradictions":
1867
- result = await safe_check(check_func, message, description, benefits, key_message)
1868
  elif check_name == "contains_key_message":
1869
- result = await safe_check(check_func, message, key_message)
1870
  elif check_name == "product_name_consistency":
1871
- result = await safe_check(check_func, message, product_name)
1872
  elif check_name == "clickbait_phrases":
1873
- result = await safe_check(check_func, message, description, benefits, key_message)
1874
  elif check_name == "abstract_claims":
1875
- result = await safe_check(check_func, message, description, benefits, key_message)
1876
  elif check_name == "cliches_and_bureaucratese":
1877
- result = await safe_check(check_func, message, description, benefits, key_message)
1878
  else:
1879
- result = await safe_check(check_func, message)
1880
  checks[check_name] = result
1881
  if result is False:
1882
  return checks
@@ -1884,7 +1687,7 @@ async def perform_checks(message, description, key_message, product_name, benefi
1884
 
1885
  # Perform remaining LLM checks
1886
  for check_name, check_func in llm_checks_group2:
1887
- result = await safe_check(check_func, message)
1888
  checks[check_name] = result
1889
  if result is False:
1890
  return checks # Stop on first failure
@@ -1897,7 +1700,7 @@ def format_checks(checks):
1897
  "forbidden_words": "Запрещенные слова",
1898
  "client_addressing": "Обращение к клиенту",
1899
  "promises": "Обещания и гарантии",
1900
- # "double_verbs": "Два глагола подряд",
1901
  "participles": "Причастия",
1902
  "adverbial_participles": "Деепричастия",
1903
  "superlative_adjectives": "Превосходная степень",
@@ -1912,7 +1715,7 @@ def format_checks(checks):
1912
  "derived_prepositions": "Производные предлоги",
1913
  "compound_sentences": "Сложноподчиненные предложения",
1914
  "dates_written_out": "Даты прописью",
1915
- # "no_word_repetitions": "Повторы слов",
1916
  # Проверки на LLM
1917
  "disconnected_sentences": "Сложные предложения без логической связи",
1918
  "synonymous_members": "Близкие по смыслу однородные члены предложения",
@@ -1979,7 +1782,7 @@ with gr.Blocks() as demo:
1979
  key_message = gr.Textbox(
1980
  label="Ключевое сообщение (предзаполненный пример можно поменять на свой)",
1981
  lines=1,
1982
- value="Повышенный льготный период при покупках у партнёров банка — до 365 дней и бесплатное годовое обслуживание."
1983
  )
1984
 
1985
  with gr.Column():
 
18
  import io
19
  from transformers import AutoTokenizer, AutoModel
20
  from utils import best_text_choice
 
 
 
21
 
22
  tokenizer = AutoTokenizer.from_pretrained("ai-forever/ru-en-RoSBERTa")
23
  model = AutoModel.from_pretrained("ai-forever/ru-en-RoSBERTa")
 
25
  unique_sms_df = pd.read_parquet('unique_texts.parquet')
26
 
27
  MISTRAL_API_KEY = os.getenv('MISTRAL_API_KEY')
 
 
 
 
28
  token = os.getenv('GITHUB_TOKEN')
29
 
 
 
30
  # Клиент для генерации сообщений
31
  client_mistral_generate = Mistral(api_key=MISTRAL_API_KEY)
32
 
33
  # Клиент для выполнения проверок
34
+ client_mistral_check = Mistral(api_key=MISTRAL_API_KEY)
 
 
 
35
 
36
  morph = pymorphy3.MorphAnalyzer()
37
 
 
188
  # except Exception as e:
189
  # return f"Ошибка при обращении к GigaChat-Pro: {e}"
190
 
191
+ def generate_message_mistral_generate(prompt, max_retries=5):
 
192
  #def generate_message_mistral_generate(prompt):
193
  # try:
194
  # messages = [SystemMessage(content=prompt)]
 
201
  retries = 0
202
  while retries < max_retries:
203
  try:
204
+ chat_response = client_mistral_generate.chat.complete(
205
+ model="mistral-large-latest",
206
+ temperature=1.0,
207
+ min_tokens=81,
208
+ max_tokens=108,
209
+ messages=[
210
+ {
211
+ "role": "user",
212
+ "content": prompt
213
+ },
214
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  )
216
+ cleaned_message = clean_message(chat_response.choices[0].message.content.strip())
 
 
217
  return cleaned_message
 
218
  except Exception as e:
219
  error_message = str(e)
 
220
  if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
221
+ wait_time = 3
222
+ print(f"Превышен лимит запросов или сервер не ответил. Ожидание {wait_time} секунд перед повторной попыткой...")
223
+ time.sleep(wait_time)
224
  retries += 1
225
  else:
226
+ print(f"Ошибка при обращении к Mistral: {e}")
 
227
  return None
228
+ print("Не удалось получить ответ от Mistral после максимального числа попыток.")
 
 
229
  return None
230
 
231
+ def generate_message_mistral_check(prompt, max_retries=5):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  #def generate_message_mistral_check(prompt):
233
  # try:
234
  # messages = [SystemMessage(content=prompt)]
 
237
  # return cleaned_message
238
  # except Exception as e:
239
  # return f"Ошибка при обращении к GigaChat-Pro: {e}"
 
240
  retries = 0
241
  while retries < max_retries:
242
  try:
243
+ chat_response = client_mistral_check.chat.complete(
244
+ model="mistral-large-latest",
245
+ temperature=0.2,
246
+ messages=[
247
+ {
248
+ "role": "user",
249
+ "content": prompt
250
+ },
251
+ ]
252
  )
253
+ cleaned_message = clean_message(chat_response.choices[0].message.content.strip())
 
 
254
  return cleaned_message
 
255
  except Exception as e:
256
+ if "Status 429" in str(e) or "Server disconnected without sending a response" in str(e):
257
+ wait_time = 3 # Можно установить фиксированную задержку
258
+ print(f"Превышен лимит запросов. Ожидание {wait_time} секунд перед повторной попыткой...")
259
+ time.sleep(wait_time)
260
+ retries += 1
261
+ else:
262
+ print(f"Ошибка при обращении к Mistral: {e}")
263
+ return None
264
+
265
+ #def generate_check_gigachat_pro(prompt):
 
 
 
 
 
 
 
 
266
  # try:
267
  # messages = [SystemMessage(content=prompt)]
268
  # res2 = chat_pro_check(messages)
 
270
  # return cleaned_message
271
  # except Exception as e:
272
  # return f"Ошибка при обращении к GigaChat-Pro: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
 
274
  # Функция для замены сокращений с 'k' или 'К' на тысячи
275
  def replace_k_with_thousands(message):
 
438
  "derived_prepositions": "Не использовать производные предлоги.",
439
  "compound_sentences": "Избегать сложноподчиненных предложений.",
440
  "dates_written_out": "Не писать даты прописью.",
441
+ "no_word_repetitions": "Избегать повторов слов.",
442
  "disconnected_sentences": "Избегать сложных предложений без логической связи.",
443
  "synonymous_members": "Не использовать близкие по смыслу однородные члены предложения.",
444
  "clickbait_phrases": "Не использовать кликбейтные фразы.",
 
463
 
464
 
465
  def notify_failed_length(message_length):
466
+ if message_length < 190:
467
+ gr.Warning(f"Сообщение слишком короткое: {message_length} знаков. Минимум 190.")
468
  return False
469
  elif message_length > 250:
470
  gr.Warning(f"Сообщение слишком длинное: {message_length} знаков. Максимум 250.")
 
477
  "forbidden_words": "Запрещенные слова",
478
  "client_addressing": "Обращение к клиенту",
479
  "promises": "Обещания и гарантии",
480
+ "double_verbs": "Два глагола подряд",
481
  "participles": "Причастия",
482
  "adverbial_participles": "Деепричастия",
483
  "superlative_adjectives": "Превосходная степень",
 
492
  "derived_prepositions": "Производные предлоги",
493
  "compound_sentences": "Сложноподчиненные предложения",
494
  "dates_written_out": "Даты прописью",
495
+ "no_word_repetitions": "Повторы слов",
496
  "disconnected_sentences": "Сложные предложения без логической связи",
497
  "synonymous_members": "Близкие по смыслу однородные члены предложения",
498
  "clickbait_phrases": "Кликбейтные фразы",
 
545
  # return last_message
546
 
547
 
548
+ def generate_message_mistral_with_retry(prompt, approach_name, description, key_message, product_name, benefits):
549
  global approach_stats
550
  last_message = None
551
  for attempt in range(20):
552
+ gr.Info(f"Итерация {attempt + 1}: генерируется сообщение...")
553
+ message = generate_message_mistral_generate(prompt)
554
  if message is None:
555
+ print("Не удалось получить сообщение от Mistral, повторная попытка...")
556
+ time.sleep(1)
557
  continue
558
  message = replace_k_with_thousands(message)
559
  message = correct_dash_usage(message)
560
  message_length = len(message)
561
  if not notify_failed_length(message_length):
562
  last_message = message
563
+ time.sleep(1)
564
  continue
565
+ checks = perform_checks(message, description, key_message, product_name, benefits)
566
  last_message = message
567
  # Инициализируем статистику для подхода, если ее нет
568
  if approach_name not in approach_stats:
 
576
  if all(checks.values()):
577
  return message
578
  prompt = append_errors_to_prompt(prompt, checks)
579
+ time.sleep(1)
580
  gr.Info("Не удалось сгенерировать сообщение, соответствующее требованиям, за 20 итераций. Возвращаем последнее сгенерированное сообщение.")
581
  return last_message
582
 
 
589
  f"Преимущества: {benefits}\n"
590
  "В тексте смс запрещено использование:\n"
591
  "- Запрещенные слова: № один, номер один, № 1, вкусный, дешёвый, продукт, спам, доступный, банкротство, долги, займ, срочно, сейчас, лучший, главный, номер 1, гарантия, успех, лидер, никакой;\n"
592
+ "- Повторы слов;\n"
593
  "- Обращение к клиенту;\n"
594
  "- Приветствие клиента;\n"
595
  "- Обещания и гарантии;\n"
 
616
  "- Узкоспециализированные термины;\n"
617
  "- Фразы, способные создать двойственное ощущение, обидеть;\n"
618
  "- Речевые клише, рекламные штампы, канцеляризмы;\n"
619
+ "Убедись, что в готовом тексте до 250, но не менее 190 знаков с пробелами. Убедись, что в готовом тексте не менее трех предложений.\n"
620
  )
621
  if key_message.strip():
622
  prompt += f"Убедись, что в готовом тексте есть следующая ключевая информация: {key_message.strip()}"
 
628
  def generate_personalization_prompt(key_message, *selected_values, prefix, suffix, product_name):
629
  prompt = f"{prefix}\n"
630
  prompt += f"Не изменяй название продукта: {product_name}.\n"
631
+ prompt += "Адаптируй, не превышая длину сообщения в 250 знаков с пробелами (но и не менее 190 знаков с пробелами), текст с учетом следующих особенностей:\n"
632
  gender, generation, psychotype = selected_values[0], selected_values[1], selected_values[2]
633
  combined_instruction = ""
634
  additional_instructions = ""
 
692
  return cleaned_prompt.strip()
693
 
694
  # Функция для постепенной генерации всех сообщений через yield
695
+ def generate_all_messages(desc, benefits, key_message, gender, generation, psychotype, business_stage, industry, opf, product_name):
696
  standard_prompt = generate_standard_prompt(desc, benefits, key_message)
697
  standard_prompt_for_display = f"Не изменяй название продукта: {product_name}.\n{standard_prompt}\nУбедись, что в готовом тексте без изменений, синонимов и перестановок слов используется наименование продукта: {product_name}.\n"
698
  approach_mapping = {
 
764
  yield selected_approaches_text_content, standard_prompt_for_display, display_personalization_prompt, None, None
765
  flag += 1
766
  prompt = add_prefix_suffix(standard_prompt, current_prefix, current_suffix, product_name)
767
+ non_personalized_message = generate_message_mistral_with_retry(prompt, approach_name, desc, key_message, product_name, benefits)
768
  non_personalized_length = len(non_personalized_message)
769
  non_personalized_display = f"{non_personalized_message}\n------\nКоличество знаков: {non_personalized_length}"
770
  if non_personalized_messages:
 
776
  non_personalized_messages, personalized_messages
777
  )
778
  full_personalized_prompt = f"{personalization_prompt}\n\nТекст для адаптации: {non_personalized_message}"
779
+ personalized_message = generate_message_mistral_with_retry(full_personalized_prompt, approach_name, desc, key_message, product_name, benefits)
780
  personalized_length = len(personalized_message)
781
  personalized_display = f"{personalized_message}\n------\nКоличество знаков: {personalized_length}"
782
  if personalized_messages:
 
787
  selected_approaches_text_content, standard_prompt_for_display, display_personalization_prompt,
788
  non_personalized_messages, personalized_messages
789
  )
790
+ time.sleep(1)
791
  save_statistics_to_github(approach_stats)
792
 
793
  def rank_messages(non_personalized_messages, personalized_messages):
 
1181
  'и', 'а', 'но', 'или', 'да', 'ни', 'как', 'так',
1182
  'в', 'на', 'под', 'над', 'за', 'к', 'до', 'по', 'из', 'у', 'о', 'про', 'для',
1183
  'не', 'вот', 'это', 'тот', 'тем', 'при', 'чем',
1184
+ 'же', 'ли', 'бы', 'то', 'р',
1185
  ])
1186
 
1187
  # Разбиваем текст на слова, удаляя знаки препинания
 
1261
  return message
1262
 
1263
  # 22. Проверка сложных предложений без логической связи
1264
+ def check_disconnected_sentences(message):
1265
  message_clean = cut_message(message)
1266
  print()
1267
  print("Проверка 22: Проверка сложных предложений без логической связи")
 
1276
  если таких предложений **нет**, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
1277
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь **только** в формате JSON с закрывающими кавычками и скобками.**'''
1278
 
1279
+ response = generate_message_mistral_check(prompt)
1280
+ time.sleep(3) # Задержка в 3 секунды между запросами
1281
  print("Mistral response:", response) # Выводим полный ответ модели
1282
  result = parse_json_response(response)
1283
  if result is not None:
 
1289
  return None
1290
 
1291
  # 23. Проверка на близкие по смыслу однородные члены
1292
+ def check_synonymous_members(message):
1293
  print()
1294
  print("Проверка 23: Проверка на близкие по смыслу однородные члены")
1295
  print()
 
1303
  если таких слов или выражений нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
1304
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1305
 
1306
+ response = generate_message_mistral_check(prompt)
1307
+ time.sleep(3)
1308
  print("Mistral response:", response)
1309
  result = parse_json_response(response)
1310
  if result is not None:
 
1317
 
1318
 
1319
  # 24. Проверка на шокирующие, экстравагантные или кликбейтные фразы
1320
+ def check_clickbait_phrases(message, description, benefits):
1321
  message_clean = cut_message(message)
1322
  print()
1323
  print()
 
1333
  3. Стандартные рекламные призывы к действию, такие как "купите сейчас" или "узнайте больше", не считаются кликбейтом, если они не преувеличивают преимущества или не используют явную манипуляцию эмоциями.
1334
  4. Не считай фразы, используемые в исходном описании продукта, кликбейтными. Исходное описание: "{description}".
1335
  5. Не считай фразы, используемые в преимуществах продукта, кликбейтными. Преимущества: "{benefits}".
 
1336
  Пример ответа:
1337
  {{"decision": false, "explanation": "Текст нейтрален и не содержит кликбейтных фраз."}}
1338
 
 
1341
 
1342
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1343
 
1344
+ response = generate_message_mistral_check(prompt)
1345
+ time.sleep(3)
1346
  print("Mistral response:", response)
1347
  result = parse_json_response(response)
1348
  if result is not None:
 
1355
 
1356
 
1357
  # 25. Проверка на абстрактные заявления без поддержки фактами
1358
+ def check_abstract_claims(message, description, benefits):
1359
  print()
1360
  print("Проверка 25: Проверка на абстрактные заявления без поддержки фактами")
1361
  print()
 
1382
  - "Снизьте финансовую нагрузку"
1383
  4. Ищи общие фразы, которые не дают представления о конкретной пользе, такие как "лучшее решение", "высокое качество", "отличный сервис", если они не сопровождаются пояснением о том, почему это так.
1384
  5. Учитывай, что в рекламных сообщениях допустимы эмоциональные и обобщённые фразы, если они достаточно конкретны для понимания аудитории, однако они должны сопровождаться фактами или подробными примерами.
1385
+ 6. Не считай фразы, используемые в исходном описании продукта, кликбейтными. Исходное описание: "{description}".
1386
+ 7. Не считай фразы, используемые в преимуществах продукта, кликбейтными. Преимущества: "{benefits}".
 
1387
 
1388
  Пример ответа:
1389
  {{"decision": false, "explanation": "Текст не содержит абстрактные утверждения без конкретики."}}
 
1393
 
1394
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1395
 
1396
+ response = generate_message_mistral_check(prompt)
1397
+ time.sleep(3)
1398
  print("Mistral response:", response)
1399
  result = parse_json_response(response)
1400
  if result is not None:
 
1407
 
1408
 
1409
  # 26. Проверка на узкоспециализированные термины
1410
+ def check_specialized_terms(message):
1411
  print()
1412
  print("Проверка 26: Проверка на узкоспециализированные термины")
1413
  print()
 
1430
 
1431
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1432
 
1433
+ response = generate_message_mistral_check(prompt)
1434
+ time.sleep(3)
1435
  print("Mistral response:", response)
1436
  result = parse_json_response(response)
1437
  if result is not None:
 
1443
  return None
1444
 
1445
  # 27. Проверка на двусмысленные или обидные фразы
1446
+ def check_offensive_phrases(message):
1447
  print()
1448
  print("Проверка 27: Проверка на двусмысленные или обидные фразы")
1449
  print()
 
1459
  если таких фраз нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
1460
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1461
 
1462
+ response = generate_message_mistral_check(prompt)
1463
+ time.sleep(3)
1464
  print("Mistral response:", response)
1465
  result = parse_json_response(response)
1466
  if result is not None:
 
1472
  return None
1473
 
1474
  # 28. Проверка на речевые клише, рекламные штампы и канцеляризмы
1475
+ def check_cliches_and_bureaucratese(message, description, benefits):
1476
  print()
1477
  print("Проверка 28: Проверка на речевые клише, рекламные штампы и канцеляризмы")
1478
  print()
 
1485
  - Информацию о ценах, скидках, акциях или условиях покупки (например, "при покупках от 100 000 рублей в месяц").
1486
  - Описания способов оформления или получения услуг (например, "оформление возможно онлайн или в офисе").
1487
  - Стандартные отраслевые термины и фразы, необходимые для понимания сообщения (например, "премиальная бизнес-карта", "Mastercard Preferred"), но **не** их использование в комбинации с общими словами, как например, "идеальное решение для вашего бизнеса".
1488
+ - Фразы, используемые в исходном описании продукта, кликбейтными. Исходное описание: "{description}".
1489
+ - Фразы, используемые в преимуществах продукта, кликбейтными. Преимущества: "{benefits}".
 
1490
  **Считай клише или канцеляризмами следующие типы выражений:**
1491
  - Избитые фразы, такие как:
1492
  - "Обеспечьте стабильность и развитие вашего бизнеса"
 
1504
  если в тексте **есть** такие выражения, **верни только** JSON {{"decision": true, "explanation": "<пояснение>"}}.
1505
  **Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
1506
 
1507
+ response = generate_message_mistral_check(prompt)
1508
+ time.sleep(3)
1509
  print("Mistral response:", response)
1510
  result = parse_json_response(response)
1511
  if result is not None:
 
1517
  return None
1518
 
1519
  # 29. Проверка на соответствие описанию предложения и ключевому сообщению
1520
+ def check_no_contradictions(message, description, key_message):
1521
  print()
1522
  print("Проверка 29: Проверка на отсутствие противоречий с описанием предложения")
1523
  print()
1524
  message_clean = cut_message(message)
1525
+ prompt = f'''Проверь, не противоречит ли следующее сообщение описанию предложения и ключевому сообщению. Учти, что сообщение является выжимкой из описания предложения и не может содержать столько же информации в том же объеме, сколько описание предложения - важно, чтобы в сообщении не было указано ложных фактов.
1526
  Описание предложения:
1527
  "{description}"
 
 
1528
  Ключевое сообщение:
1529
  "{key_message}"
1530
  Сообщение:
1531
  "{message}"
1532
+ Если сообщение не содержит фактов, которые отсутствуют в описании предложения и ключевом сообщении, **верни только** JSON {{"decision": false, "explanation": "Противоречий не обнаружено."}}.
1533
+ Если сообщение содержит факты, которые отсутствую�� в описании предложения и ключевом сообщении, **верни только** JSON {{"decision": true, "explanation": "<описание противоречий>"}}.
1534
  **Не добавляй никакого дополнительного текста. Отвечай только в формате JSON с закрывающими кавычками и скобками.**'''
1535
 
1536
+ response = generate_message_mistral_check(prompt)
1537
+ time.sleep(3)
1538
  print("Mistral response:", response)
1539
  result = parse_json_response(response)
1540
  if result is not None:
 
1546
  return None
1547
 
1548
  # 30. Проверка на наличие ключевого сообщения
1549
+ def check_contains_key_message(message, key_message):
1550
  print()
1551
  print("Проверка 30: Проверка на наличие ключевого сообщения")
1552
  print()
 
1560
  Если сообщение **не содержит всю** информацию из ключевого текста, **верни только** JSON {{"decision": true, "explanation": "Ключевое текст отсутствует."}}.
1561
  **Не добавляй никакого дополнительного текста. Отвечай только в формате JSON с закрывающими кавычками и скобками.**'''
1562
 
1563
+ response = generate_message_mistral_check(prompt)
1564
+ time.sleep(3)
1565
  print("Mistral response:", response)
1566
  result = parse_json_response(response)
1567
  if result is not None:
 
1573
  return None
1574
 
1575
  # 31. Проверка на точное совпадение названий продуктов
1576
+ def check_product_name_consistency(message, product_name):
1577
  print()
1578
  print("Проверка 31: Проверка на точное совпадение названий продуктов")
1579
  print()
 
1590
  Если название продукта **не** совпадает, **верни только** JSON {{"decision": true, "explanation": "<описание несоответствия>"}}.
1591
  **Не добавляй никакого дополнительного текста. Ответ должен быть только в формате JSON с закрывающими кавычками и скобками.**'''
1592
 
1593
+ response = generate_message_mistral_check(prompt)
1594
+ time.sleep(3)
1595
  print("Mistral response:", response)
1596
  result = parse_json_response(response)
1597
  if result is not None:
 
1605
  # ФУНКЦИИ ПРОВЕРОК (КОНЕЦ)
1606
 
1607
 
1608
+ def safe_check(func, *args):
1609
  try:
1610
+ return func(*args)
 
 
 
1611
  except Exception as e:
1612
+ # Optionally, you can log the exception here if needed
1613
+ print(f"Ошибка в {func.__name__}: {e}")
1614
+ return None # Indicate that the check could not be performed
1615
 
1616
+ def perform_checks(message, description, key_message, product_name, benefits):
1617
  checks = {}
1618
 
1619
  # 2. Morphological checks using pymorphy3
1620
  morphological_checks = [
 
1621
  ("forbidden_words", check_forbidden_words),
 
 
 
 
 
 
 
1622
  ("client_addressing", check_no_greeting),
1623
  ("promises", check_no_promises),
1624
+ ("double_verbs", check_no_double_verbs),
1625
  ("participles", check_no_participles),
1626
+ ("adverbial_participles", check_no_adverbial_participles),
1627
+ ("superlative_adjectives", check_no_superlative_adjectives),
1628
  ("passive_voice", check_no_passive_voice),
1629
  ("written_out_ordinals", check_no_written_out_ordinals),
1630
  ("subordinate_clauses_chain", check_no_subordinate_clauses_chain),
1631
  ("repeating_conjunctions", check_no_repeating_conjunctions),
1632
+ ("introductory_phrases", check_no_introductory_phrases),
1633
+ ("amplifiers", check_no_amplifiers),
1634
  ("time_parasites", check_no_time_parasites),
1635
+ ("multiple_nouns", check_no_multiple_nouns),
1636
+ ("derived_prepositions", check_no_derived_prepositions),
1637
+ ("compound_sentences", check_no_compound_sentences),
1638
  ("dates_written_out", check_no_dates_written_out),
1639
+ ("no_word_repetitions", check_no_word_repetitions),
1640
  ]
1641
 
1642
  # 3. LLM checks: check_clickbait_phrases, check_abstract_claims, check_cliches_and_bureaucratese
1643
  llm_checks_group1 = [
1644
+ ("product_name_consistency", check_product_name_consistency),
1645
  ("no_contradictions", check_no_contradictions),
1646
+ ("clickbait_phrases", check_clickbait_phrases),
1647
  ("abstract_claims", check_abstract_claims),
1648
  ("cliches_and_bureaucratese", check_cliches_and_bureaucratese),
1649
+ ("contains_key_message", check_contains_key_message)
 
 
 
1650
  ]
1651
 
1652
  # 4. Remaining LLM checks
1653
  llm_checks_group2 = [
 
1654
  ("disconnected_sentences", check_disconnected_sentences),
1655
+ ("synonymous_members", check_synonymous_members),
1656
+ ("specialized_terms", check_specialized_terms),
1657
+ ("offensive_phrases", check_offensive_phrases),
1658
  ]
1659
 
1660
  # Perform morphological checks
1661
  for check_name, check_func in morphological_checks:
1662
+ result = safe_check(check_func, message)
1663
  checks[check_name] = result
1664
  if result is False:
1665
  return checks # Stop on first failure
 
1667
  # Perform LLM checks group 1
1668
  for check_name, check_func in llm_checks_group1:
1669
  if check_name == "no_contradictions":
1670
+ result = safe_check(check_func, message, description, key_message)
1671
  elif check_name == "contains_key_message":
1672
+ result = safe_check(check_func, message, key_message)
1673
  elif check_name == "product_name_consistency":
1674
+ result = safe_check(check_func, message, product_name)
1675
  elif check_name == "clickbait_phrases":
1676
+ result = safe_check(check_func, message, description, benefits)
1677
  elif check_name == "abstract_claims":
1678
+ result = safe_check(check_func, message, description, benefits)
1679
  elif check_name == "cliches_and_bureaucratese":
1680
+ result = safe_check(check_func, message, description, benefits)
1681
  else:
1682
+ result = safe_check(check_func, message)
1683
  checks[check_name] = result
1684
  if result is False:
1685
  return checks
 
1687
 
1688
  # Perform remaining LLM checks
1689
  for check_name, check_func in llm_checks_group2:
1690
+ result = safe_check(check_func, message)
1691
  checks[check_name] = result
1692
  if result is False:
1693
  return checks # Stop on first failure
 
1700
  "forbidden_words": "Запрещенные слова",
1701
  "client_addressing": "Обращение к клиенту",
1702
  "promises": "Обещания и гарантии",
1703
+ "double_verbs": "Два глагола подряд",
1704
  "participles": "Причастия",
1705
  "adverbial_participles": "Деепричастия",
1706
  "superlative_adjectives": "Превосходная степень",
 
1715
  "derived_prepositions": "Производные предлоги",
1716
  "compound_sentences": "Сложноподчиненные предложения",
1717
  "dates_written_out": "Даты прописью",
1718
+ "no_word_repetitions": "Повторы слов",
1719
  # Проверки на LLM
1720
  "disconnected_sentences": "Сложные предложения без логической связи",
1721
  "synonymous_members": "Близкие по смыслу однородные члены предложения",
 
1782
  key_message = gr.Textbox(
1783
  label="Ключевое сообщение (предзаполненный пример можно поменять на свой)",
1784
  lines=1,
1785
+ value="Повышенный льготный период при покупках у партнёров банка — до 365 дней и бесплатное годовое обслуживание кредитной бизнес-карты."
1786
  )
1787
 
1788
  with gr.Column():