import openai from openai import OpenAI import difflib prompt_check_error_message = """ Ты — лингвистический анализатор. Тебе будут дано описание ошибки в тексте. Задача: - Если в сообщении говорится что ошибки нет или что это ошибка форматирования - верни 0 - В остальных случаях возвращай 1 Обрати внимаение, если ты бы охарактеризовал ошибку в тексте, как ошибку форматирования или оформления, то нужно вернуть 0!!! Сообщение об ошибке: {} Вывод В ФОРМАТЕ JSON без комментариев: {{"result": 0|1}} """ prompt_is_there_error = """ Ты — лингвистический анализатор. Тебе будут даны две текстовые строки: 1. Оригинальный текст (точно без ошибок) 2. Текст для проверки (может содержать ошибку или альтернативное написание) Задача: - Сравни второй текст с оригиналом на предмет наличия ошибок (орфографических, пунктуационных, грамматических) - Если во втором тексте есть орфографическая, пунктуационная или грамматическая ошибка(отличается от оригинала и это не допустимый вариант написания) — верни 1 - Если текст совпадает с оригиналом или отличается только допустимыми вариантами написания (синонимы, альтернативная пунктуация и т.п.) или же присутствует ошибка формативарования — верни 0 Обрати внимаение, если ты бы охарактеризовал ошибку в тексте, как ошибку форматирования или оформления, то нужно вернуть 0!!! Оригинальный текст: {} Текст для проверки: {} Вывод В ФОРМАТЕ JSON без комментариев: {{"result": 0|1}} """ prompt_fix_text_gpt = """ Исправь ошибки в данном тексте. Текст в makrdown и должен в результате остаться в markdown. Исправляй грамматические, пунктуационные и орфоргафические ошибки. Логические, лексические, стилистические ошибки, а также ошибки форматирования (напрмер неправильные переносы строки) ошибками не считаются!!! Их исправлять не надо Буква "е" вместо буквы "ё" не считается ошибкой. Не исправляй ошибки форматрирования!!! В текст заранее удалены все ненужны переносы, это сделано осознанно! Верни только текст, никаких комментариев от себя не оставляй. ### Пример 1: input text: Это все мое, чашки вилки и ложи Твой ответ: Это все мое: чашки, вилки и ложки Теперь твоя очередь: input text: {} """ prompt_compare_get_comment = """ Ты должен писать комментарий об ошибке в тексте. Тебе дан кусок текст, в котором есть одна или несколько ошибок и этот же кусок текста с исправленными ошибками. Твоя задача - кратко описать все ошибки, которые есть в тексте. Пиши кратко, не больше одного-двух предложений. Отвечай на русском языке. ### Пример 1: original text: "кросивую, сабаку" corrected text: "красивую собаку" Твой ответ: Орфографические ошибки: "кросивую" -> "красивую", "сабаку" -> "собаку", лишняя запятая после "красивую". ### Пример 2: original: "я решил пойти, в" corrected text: "я решил пойти в" Твой ответ: Опечатка: повтор пробела, лишняя запятая после слова "пойти". Теперь твоя очередь: original: "{}" corrected text: "{}" """ prompt = """ Ты должен писать комментарии об ошибках в тексте. Тебе дан кусок текст, в котором есть ошибка, контекст, в котором стоит это слово и сообщение об ошибке. Твоя задача - кратко описать суть ошибки и, если необходимо, исправить её. Исправляй только ту ошибку, на которую указывает сообщение. Отвечай на русском языке. ### Пример 1: Кусок текста: "кросивую" Сообщение об ошибке: "Возможно найдена орфографическая ошибка." Текст: "...т! Сегодня я был в парке и встретил там кросивую собаку. Она повиляла хвостом и побежа..." Твой ответ: Орфографическая ошибка в слове "кросивую" - правильно "красивую". ### Пример 2: Кусок текста: " " Сообщение об ошибке: "Повтор пробела." Текст: "...ретил там кросивую собаку. Она повиляла хвостом и побежала к речке. Я решил что ..." Твой ответ: Обнаружен повтор пробела между словами. Теперь твоя очередь: Кусок текста: "{}" Сообщение об ошибке: "{}" Текст: "{}" """ def get_gpt_response_openai(inp): response = openai.ChatCompletion.create( model="openai/gpt-4.1-mini", messages=[ {"role": "user", "content": inp} ] ) return response.choices[0].message['content'] def get_gpt_response_vsegpt(inp): client = OpenAI( api_key='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', base_url='https://openrouter.ai/api/v1' ) response = client.chat.completions.create( model="openai/gpt-4.1-mini", messages=[{"role": "user", "content": inp}] ).choices[0].message.content return response def get_gpt_response(inp, client_name="vsegpt"): if client_name == "openai": return get_gpt_response_openai(inp) elif client_name == "vsegpt": return get_gpt_response_vsegpt(inp) else: raise ValueError(f"Unsupported client: {client_name}") def find_corrected_positions(original, corrected): matcher = difflib.SequenceMatcher(None, original, corrected) changes = [] for opcode in matcher.get_opcodes(): tag, i1, i2, j1, j2 = opcode if tag != 'equal': changes.append({ 'original': (i1, i2), 'corrected': (j1, j2), 'operation': tag }) return changes def get_piece_of_text_bounds(s, start, end): if start != 0: start -= 1 while start != 0 and s[start] not in [' ', "\n", '\t']: start -= 1 if s[start] in [' ', "\n", '\t']: start += 1 if end < len(s) - 1: end += 1 while end < len(s) - 1 and s[end] not in [' ', "\n", '\t']: end += 1 if end == len(s) - 1: end += 1 return start, end def add_comments_to_text(text, errors, add_errors=False): errors = sorted(errors, key=lambda x: x['end']) shift = 0 for i, error in enumerate(errors, 1): error['start'] += shift error['end'] += shift inp = f"({i})" end_place = error["end"] for i in range(end_place, end_place - 5, -1): symbol = text[i - 1] if symbol.isalpha() or symbol in ",.!?:;": break error["end"] -= 1 if add_errors: inp = inp[:-1] + ' - ' + error['message'] + ')' text = text[:error['end']] + inp + text[error['end']:] error["end"] += len(inp) shift += len(inp) return text def check_text(text, tool, mode="chat_gpt", highlight_mode=False, add_errors=False): if mode == "tool": return check_text_with_tool(text, tool, add_errors=add_errors) else: if highlight_mode: return check_text_chat_gpt_highlight_mode(text, add_errors=add_errors) else: return check_text_chat_gpt(text, add_errors=add_errors) def check_text_chat_gpt(text, fixed_text=None, add_errors=False, *args, **kwargs): if fixed_text is None: fixed_text = get_gpt_response(prompt_fix_text_gpt.format(text), "vsegpt") changes = find_corrected_positions(text, fixed_text) errors = [] for change in changes: start_orig, end_orig = get_piece_of_text_bounds(text, change['original'][0], change['original'][1]) start_corr, end_corr = get_piece_of_text_bounds(fixed_text, change['corrected'][0], change['corrected'][1]) inp = prompt_compare_get_comment.format(text[start_orig:end_orig], fixed_text[start_corr:end_corr]) errors.append({ 'start': start_orig, 'end': end_orig, 'message': get_gpt_response(inp, client_name="vsegpt"), }) text_with_comments = add_comments_to_text(text, errors, add_errors=add_errors) return text_with_comments, errors def check_text_chat_gpt_highlight_mode(text, fixed_text=None, add_errors=False, *args, **kwargs): if fixed_text is None: fixed_text = get_gpt_response(prompt_fix_text_gpt.format(text), "vsegpt") text = text.replace("ё", "е") fixed_text = fixed_text.replace("ё", "е") changes = find_corrected_positions(text, fixed_text) bounds_init = [] for change in changes: start_orig, end_orig = get_piece_of_text_bounds(text, change['original'][0], change['original'][1]) start_corr, end_corr = get_piece_of_text_bounds(fixed_text, change['corrected'][0], change['corrected'][1]) bounds_init.append({"start_orig": start_orig, "end_orig": end_orig, "start_corr": start_corr, "end_corr": end_corr}) bounds_init = sorted(bounds_init, key=lambda x: x["start_orig"]) bounds_result = [bounds_init[0]] if len(bounds_init) > 0 else [] for bound in bounds_init[1:]: if bounds_result[-1]["end_orig"] >= bound["start_orig"]: bounds_result[-1]["end_orig"] = max(bounds_result[-1]["end_orig"], bound["end_orig"]) bounds_result[-1]["end_corr"] = max(bounds_result[-1]["end_corr"], bound["end_corr"]) else: bounds_result.append(bound.copy()) errors = [] for bound in bounds_result: orig_piece = text[bound["start_orig"]:bound["end_orig"]] fixed_piece = fixed_text[bound["start_corr"]:bound["end_corr"]] if "0" in get_gpt_response(prompt_is_there_error.format(orig_piece, fixed_piece)): continue inp = prompt_compare_get_comment.format(orig_piece, fixed_piece) error = { 'start': bound["start_orig"], 'end': bound["end_orig"], 'message': get_gpt_response(inp, client_name="vsegpt"), } #if "1" in get_gpt_response(prompt_check_error_message.format(error["message"])): errors.append(error) text_with_comments = add_comments_to_text(text, errors, add_errors=add_errors) return text_with_comments, errors def check_text_with_tool(text, tool, add_errors=False): matches = tool.check(text) errors = [] for match in matches: inp = prompt.format(text[match.offset:match.offset + match.errorLength], match.message, match.context) error_info = { 'start': match.offset, 'end': match.offset + match.errorLength, 'message': get_gpt_response(inp, client_name="vsegpt"), } errors.append(error_info) text_with_comments = add_comments_to_text(text, errors, add_errors=add_errors) return text_with_comments, errors