""" Базовый класс для всех стратегий чанкинга. """ from abc import ABC, abstractmethod from ntr_fileparser import ParsedDocument from ..models import Chunk, DocumentAsEntity, LinkerEntity class ChunkingStrategy(ABC): """ Базовый абстрактный класс для всех стратегий чанкинга. """ @abstractmethod def chunk(self, document: ParsedDocument, doc_entity: DocumentAsEntity | None = None) -> list[LinkerEntity]: """ Разбивает документ на чанки в соответствии со стратегией. Args: document: ParsedDocument для извлечения текста doc_entity: Опциональная сущность документа для привязки чанков. Если не указана, будет создана новая. Returns: list[LinkerEntity]: Список сущностей (документ, чанки, связи) """ raise NotImplementedError("Стратегия чанкинга должна реализовать метод chunk") def dechunk(self, chunks: list[LinkerEntity], repository: 'EntityRepository' = None) -> str: """ Собирает документ из чанков и связей. Базовая реализация сортирует чанки по chunk_index и объединяет их тексты, сохраняя структуру параграфов и избегая дублирования текста. Args: chunks: Список отфильтрованных чанков в случайном порядке repository: Репозиторий сущностей для получения дополнительной информации (может быть None) Returns: Восстановленный текст документа """ import re # Проверяем, есть ли чанки для сборки if not chunks: return "" # Отбираем только чанки valid_chunks = [c for c in chunks if isinstance(c, Chunk)] # Сортируем чанки по chunk_index sorted_chunks = sorted(valid_chunks, key=lambda c: c.chunk_index or 0) # Собираем текст документа с учетом структуры параграфов result_text = "" for chunk in sorted_chunks: # Получаем текст чанка (предпочитаем text, а не in_search_text для избежания дублирования) chunk_text = chunk.text if hasattr(chunk, 'text') and chunk.text else "" # Добавляем текст чанка с сохранением структуры параграфов if result_text and result_text[-1] != "\n" and chunk_text and chunk_text[0] != "\n": result_text += " " result_text += chunk_text # Пост-обработка результата # Заменяем множественные переносы строк на одиночные result_text = re.sub(r'\n+', '\n', result_text) # Заменяем множественные пробелы на одиночные result_text = re.sub(r' +', ' ', result_text) # Убираем пробелы перед переносами строк result_text = re.sub(r' +\n', '\n', result_text) # Убираем пробелы после переносов строк result_text = re.sub(r'\n +', '\n', result_text) # Убираем лишние переносы строк в начале и конце текста result_text = result_text.strip() return result_text