# -*- coding: utf-8 -*- """ Модуль проверки орфографии и пунктуации в тексте документа. Разбивает документ на чанки, обрабатывает каждый чанк через GigaChat, собирает словарь замен (старое -> новое) и список изменений. АДАПТАЦИЯ: теперь функция принимает промпт и конфиг как аргументы. """ import asyncio import logging import re from core.utils.text_utils import split_into_chunks from core.services.giga_client import GigaClient logger = logging.getLogger(__name__) async def check_spelling( giga: GigaClient, original_text: str, prompt_text: str, config, ) -> tuple[dict, list[str]]: """ Проверяет орфографию документа, возвращает замены и список изменений. Аргументы: giga (GigaClient): клиент GigaChat original_text (str): исходный текст документа prompt_text (str): содержимое промпта проверки орфографии config (BotConfig): объект конфигурации Возвращает: tuple: (словарь_замен, список_изменений_в_виде_строк) В случае, если парсинг изменений не удался, возвращается: ({"_raw": сырой_ответ_LLM}, ["Сообщение для пользователя"]) """ if not prompt_text: logger.error("Промпт проверки орфографии не загружен") return {}, ["❌ Промпт проверки орфографии не загружен."] # Параметры чанкинга из конфига chunk_size_tokens = config.chunk_size_tokens overlap_tokens = config.overlap_tokens approx_chunk_chars = config.chunking_approx_chunk_chars approx_overlap_chars = config.chunking_approx_overlap_chars # Разбиваем текст на чанки chunks = split_into_chunks( original_text, chunk_size_tokens=chunk_size_tokens, overlap_tokens=overlap_tokens, approx_chunk_chars=approx_chunk_chars, approx_overlap_chars=approx_overlap_chars, strategy="recursive_split_by_sentences" ) logger.info(f"Разбито на {len(chunks)} чанков для проверки орфографии") all_replacements = {} all_changes = [] temperature = getattr(config, 'spellcheck_temperature', 0.1) # Обрабатываем каждый чанк for idx, chunk in enumerate(chunks): logger.info(f"Обработка чанка {idx+1}/{len(chunks)}") full_prompt = f"{prompt_text}\n\nТЕКСТ ДЛЯ ПРОВЕРКИ (часть {idx+1} из {len(chunks)}):\n{chunk}" try: response = await giga.chat( history=[], query=full_prompt, system_prompt=None, file_id=None, temperature=temperature ) corrected_match = re.search(r'\[CORRECTED\](.*?)\[/CORRECTED\]', response, re.DOTALL) changes_match = re.search(r'\[CHANGES\](.*?)\[/CHANGES\]', response, re.DOTALL) if changes_match: changes_text = changes_match.group(1).strip() for line in changes_text.split('\n'): line = line.strip() if not line or line.startswith('#'): continue patterns = [ r'было\s*["«](.+?)["»]\s*[→-]>\s*стало\s*["«](.+?)["»]', r'было\s*:\s*["«](.+?)["»]\s*[→-]>\s*стало\s*:\s*["«](.+?)["»]', r'было\s*["«](.+?)["»]\s*[→-]>\s*(.+)', r'было\s*:\s*(.+?)\s*[→-]>\s*стало\s*:\s*(.+)', r'было\s*(.+?)\s*[→-]>\s*стало\s*(.+)' ] match = None for pattern in patterns: match = re.search(pattern, line, re.IGNORECASE) if match: old, new = match.group(1).strip(), match.group(2).strip() break if match: all_replacements[old] = new all_changes.append(f'было "{old}" → стало "{new}"') else: all_changes.append(line) # Если список изменений не распарсился, но есть исправленный текст if corrected_match and (not changes_match or not changes_match.group(1).strip()): logger.warning(f"Не удалось распарсить список изменений для чанка {idx+1}, возвращаем сырой ответ") return {"_raw": response}, [ f"⚠️ Не удалось распарсить список изменений (чанк {idx+1}). Вот сырой ответ LLM:\n{response}" ] except Exception as e: logger.error(f"Ошибка при проверке чанка {idx+1}: {e}", exc_info=True) all_changes.append(f"⚠️ Ошибка в части {idx+1}: {e}") await asyncio.sleep(0.5) # Удаляем дубликаты unique_replacements = {} unique_changes = [] seen = set() for old, new in all_replacements.items(): key = f"{old}→{new}" if key not in seen: seen.add(key) unique_replacements[old] = new for change in all_changes: if change not in seen: seen.add(change) unique_changes.append(change) return unique_replacements, unique_changes