File size: 5,954 Bytes
a85d6bf
5884230
a85d6bf
5884230
 
0067c9d
bf93cc0
0067c9d
 
 
 
5884230
a85d6bf
0067c9d
 
 
 
5884230
0067c9d
 
 
5884230
a85d6bf
0067c9d
 
 
 
 
 
5884230
0067c9d
5884230
90e6b4c
 
a85d6bf
 
 
 
 
90e6b4c
 
 
 
 
aa38fcf
 
 
a85d6bf
aa38fcf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a85d6bf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aa38fcf
a85d6bf
 
 
aa38fcf
 
a85d6bf
 
 
 
 
 
 
 
90e6b4c
a85d6bf
 
 
 
90e6b4c
a85d6bf
aa38fcf
a85d6bf
 
90e6b4c
 
 
 
 
 
a85d6bf
90e6b4c
822ef8c
90e6b4c
a85d6bf
90e6b4c
 
 
822ef8c
90e6b4c
f85ad1c
a85d6bf
5884230
a85d6bf
c81fd8c
 
bf93cc0
822ef8c
c81fd8c
 
 
bf93cc0
822ef8c
a85d6bf
 
822ef8c
 
90e6b4c
 
 
 
 
 
 
 
 
 
 
 
a85d6bf
90e6b4c
 
 
 
a85d6bf
90e6b4c
a85d6bf
 
822ef8c
a85d6bf
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
from llama_index.core.text_splitter import SentenceSplitter
from llama_index.core import Document
from config import CHUNK_SIZE, CHUNK_OVERLAP
from my_logging import log_message

def create_table_content(table_data):
    """Create formatted content from table data"""
    doc_id = table_data.get('document_id', table_data.get('document', 'Неизвестно'))
    table_num = table_data.get('table_number', 'Неизвестно')
    table_title = table_data.get('table_title', 'Неизвестно')
    section = table_data.get('section', 'Неизвестно')
    
    # Header section
    content = f"Таблица: {table_num}\n"
    content += f"Название: {table_title}\n"
    content += f"Документ: {doc_id}\n"
    content += f"Раздел: {section}\n"
    
    headers = table_data.get('headers', [])
    if headers:
        content += f"\nЗаголовки: {' | '.join(headers)}\n"
    
    # Data section
    if 'data' in table_data and isinstance(table_data['data'], list):
        content += "\nДанные таблицы:\n"
        for row_idx, row in enumerate(table_data['data'], start=1):
            if isinstance(row, dict):
                row_text = " | ".join([f"{k}: {v}" for k, v in row.items() if v])
                content += f"Строка {row_idx}: {row_text}\n"
    
    return content


def chunk_table_document(doc, chunk_size=None, chunk_overlap=None):
    """
    Smart table chunking:
    - Small tables: keep whole
    - Large tables: split by row-blocks, preserve headers in each chunk
    """
    if chunk_size is None:
        chunk_size = CHUNK_SIZE
    if chunk_overlap is None:
        chunk_overlap = CHUNK_OVERLAP
    
    table_num = doc.metadata.get('table_number', 'unknown')
    doc_id = doc.metadata.get('document_id', 'unknown')
    
    # Parse table structure
    lines = doc.text.strip().split('\n')
    
    table_header_lines = []
    data_rows = []
    in_data = False
    
    for line in lines:
        if line.startswith('Данные таблицы:'):
            in_data = True
            table_header_lines.append(line)
        elif in_data and line.startswith('Строка'):
            data_rows.append(line)
        elif not in_data:
            table_header_lines.append(line)
    
    table_header = '\n'.join(table_header_lines) + '\n'
    
    # If no data rows or small table, use standard splitting
    if not data_rows or len(doc.text) < chunk_size * 1.5:
        log_message(f"  📊 Таблица {table_num}: малая, без разбиения")
        return [doc]
    
    # Row-block chunking for large tables
    log_message(f"  📋 Таблица {table_num}: {len(data_rows)} строк → row-block chunking")
    
    header_size = len(table_header)
    available_size = chunk_size - header_size - 100  # Reserve space
    
    text_chunks = []
    current_chunk_rows = []
    current_size = 0
    
    for row in data_rows:
        row_size = len(row) + 1
        
        # Check if adding this row exceeds limit
        if current_size + row_size > available_size and current_chunk_rows:
            # Create chunk with header + rows
            chunk_text = table_header + '\n'.join(current_chunk_rows)
            text_chunks.append(chunk_text)
            
            # Overlap: keep last 2 rows for context continuity
            overlap_count = min(2, len(current_chunk_rows))
            current_chunk_rows = current_chunk_rows[-overlap_count:]
            current_size = sum(len(r) + 1 for r in current_chunk_rows)
        
        current_chunk_rows.append(row)
        current_size += row_size
    
    # Final chunk
    if current_chunk_rows:
        chunk_text = table_header + '\n'.join(current_chunk_rows)
        text_chunks.append(chunk_text)
    
    log_message(f"  ✂️ Таблица {table_num}{len(text_chunks)} чанков")
    
    # Create Document objects
    chunked_docs = []
    for i, chunk_text in enumerate(text_chunks):
        chunk_metadata = doc.metadata.copy()
        chunk_metadata.update({
            "chunk_id": i,
            "total_chunks": len(text_chunks),
            "chunk_size": len(chunk_text),
            "is_chunked": True
        })
        
        chunked_doc = Document(
            text=chunk_text,
            metadata=chunk_metadata
        )
        chunked_docs.append(chunked_doc)
    
    return chunked_docs


def table_to_document(table_data, document_id=None):
    """Convert table data to Document, with smart chunking if needed"""
    if not isinstance(table_data, dict):
        return []
    
    doc_id = document_id or table_data.get('document_id') or table_data.get('document', 'Неизвестно')
    table_num = table_data.get('table_number', 'Неизвестно')
    table_title = table_data.get('table_title', 'Неизвестно')
    section = table_data.get('section', 'Неизвестно')
    
    table_rows = table_data.get('data', [])
    if not table_rows:
        log_message(f"⚠️ Таблица {table_num} пропущена: нет данных")
        return []
    
    content = create_table_content(table_data)
    content_size = len(content)
    
    base_doc = Document(
        text=content,
        metadata={
            "type": "table",
            "table_number": table_num,
            "table_title": table_title,
            "document_id": doc_id,
            "section": section,
            "section_id": section,
            "total_rows": len(table_rows),
            "content_size": content_size
        }
    )
    
    # Apply smart chunking if too large
    if content_size > CHUNK_SIZE:
        log_message(f"📊 CHUNKING: Таблица {table_num} | {content_size} > {CHUNK_SIZE}")
        return chunk_table_document(base_doc)
    else:
        log_message(f"✓ Таблица {table_num} добавлена целиком ({content_size} символов)")
        return [base_doc]