VietCat commited on
Commit
3670aeb
·
1 Parent(s): 0f13c77

update content_summary in chunking

Browse files
Files changed (5) hide show
  1. app/config.py +1 -1
  2. app/law_document_chunker.py +110 -10
  3. app/llm.py +2 -0
  4. app/main.py +1 -3
  5. app/supabase_db.py +2 -3
app/config.py CHANGED
@@ -40,7 +40,7 @@ class Settings(BaseSettings):
40
 
41
  # LLM (chat/completion) provider/model
42
  llm_provider: str = os.getenv("LLM_PROVIDER", "gemini") or ""
43
- llm_model: str = os.getenv("LLM_MODEL", "gemini-1.5-flash-latest") or ""
44
  # Embedding provider/model
45
  embedding_provider: str = os.getenv("EMBEDDING_PROVIDER", "gemini") or ""
46
  embedding_model: str = os.getenv("EMBEDDING_MODEL", "models/embedding-001") or ""
 
40
 
41
  # LLM (chat/completion) provider/model
42
  llm_provider: str = os.getenv("LLM_PROVIDER", "gemini") or ""
43
+ llm_model: str = os.getenv("LLM_MODEL", "gemini-2.5-flash") or ""
44
  # Embedding provider/model
45
  embedding_provider: str = os.getenv("EMBEDDING_PROVIDER", "gemini") or ""
46
  embedding_model: str = os.getenv("EMBEDDING_MODEL", "models/embedding-001") or ""
app/law_document_chunker.py CHANGED
@@ -133,7 +133,7 @@ class LawDocumentChunker:
133
 
134
  def _create_chunk_metadata(self, content: str, level: str, level_value: Optional[str],
135
  parent_id: Optional[str], vanbanid: int,
136
- document_title: str) -> ChunkMetadata:
137
  """Tạo metadata cho chunk."""
138
  chunk_id = str(uuid.uuid4())
139
 
@@ -145,17 +145,112 @@ class LawDocumentChunker:
145
  document_title=document_title
146
  )
147
 
148
- # Điền metadata theo cấp độ
149
  if level == "DIEU" and level_value:
150
  metadata.article_number = int(level_value) if level_value.isdigit() else None
151
- metadata.article_title = content.strip()
152
  elif level == "KHOAN" and level_value:
153
  metadata.clause_number = level_value
154
  elif level == "DIEM" and level_value:
155
  metadata.sub_clause_letter = level_value
156
 
 
 
 
 
157
  return metadata
158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  def _split_into_chunks(self, text: str, chunk_size: int, overlap: int) -> List[str]:
160
  """Chia text thành các chunk với overlap."""
161
  chunks = []
@@ -228,7 +323,8 @@ class LawDocumentChunker:
228
  current_level_value,
229
  current_parent,
230
  vanbanid,
231
- document_title
 
232
  )
233
  chunks.append(metadata)
234
 
@@ -260,7 +356,8 @@ class LawDocumentChunker:
260
  current_level_value,
261
  current_parent,
262
  vanbanid,
263
- document_title
 
264
  )
265
  chunks.append(metadata)
266
 
@@ -278,7 +375,8 @@ class LawDocumentChunker:
278
  current_level_value,
279
  current_parent,
280
  vanbanid,
281
- document_title
 
282
  )
283
  chunks.append(metadata)
284
 
@@ -322,9 +420,11 @@ class LawDocumentChunker:
322
 
323
  for i, chunk in enumerate(chunks, 1):
324
  try:
325
- # Tạo embedding - TẠM THỜI COMMENT LẠI ĐỂ TEST
326
- # embedding = await self.embedding_client.create_embedding(chunk.content)
327
- embedding = [0.0] * 768 # Placeholder embedding cho test
 
 
328
 
329
  # Chuẩn bị data cho Supabase
330
  chunk_dict = {
@@ -338,7 +438,7 @@ class LawDocumentChunker:
338
  'article_title': chunk.article_title,
339
  'clause_number': chunk.clause_number,
340
  'sub_clause_letter': chunk.sub_clause_letter,
341
- 'context_summary': chunk.context_summary
342
  }
343
 
344
  # Lưu ngay lập tức vào Supabase
 
133
 
134
  def _create_chunk_metadata(self, content: str, level: str, level_value: Optional[str],
135
  parent_id: Optional[str], vanbanid: int,
136
+ document_title: str, chunk_stack: List[Tuple[str, str, Optional[str]]] = []) -> ChunkMetadata:
137
  """Tạo metadata cho chunk."""
138
  chunk_id = str(uuid.uuid4())
139
 
 
145
  document_title=document_title
146
  )
147
 
148
+ # Điền metadata từ chunk hiện tại
149
  if level == "DIEU" and level_value:
150
  metadata.article_number = int(level_value) if level_value.isdigit() else None
151
+ metadata.article_title = content.split('\n')[0].strip() if content else ""
152
  elif level == "KHOAN" and level_value:
153
  metadata.clause_number = level_value
154
  elif level == "DIEM" and level_value:
155
  metadata.sub_clause_letter = level_value
156
 
157
+ # Điền metadata từ parent chunks nếu có
158
+ if chunk_stack and parent_id:
159
+ self._fill_metadata_from_parents(metadata, chunk_stack, parent_id)
160
+
161
  return metadata
162
 
163
+ def _fill_metadata_from_parents(self, metadata: ChunkMetadata, chunk_stack: List[Tuple[str, str, Optional[str]]], parent_id: str):
164
+ """
165
+ Điền metadata từ parent chunks (Điều, Khoản) nếu chunk hiện tại có cha hoặc ông là Điều/Khoản.
166
+ """
167
+ # Tìm tất cả parent chunks
168
+ parent_chunks = []
169
+ current_parent = parent_id
170
+
171
+ # Tìm tất cả parents trong stack
172
+ for chunk_id, level, level_value in reversed(chunk_stack):
173
+ if chunk_id == current_parent:
174
+ parent_chunks.append((level, level_value))
175
+ # Tìm parent của parent này
176
+ for parent_chunk_id, parent_level, parent_level_value in reversed(chunk_stack):
177
+ if parent_chunk_id == chunk_id:
178
+ current_parent = parent_chunk_id
179
+ break
180
+
181
+ # Điền metadata từ parents
182
+ for level, level_value in parent_chunks:
183
+ if level == "DIEU" and level_value:
184
+ if not metadata.article_number: # Chỉ điền nếu chưa có
185
+ metadata.article_number = int(level_value) if level_value.isdigit() else None
186
+ if not metadata.article_title: # Chỉ điền nếu chưa có
187
+ # Lấy title từ content của parent chunk
188
+ for chunk_id, parent_level, parent_level_value in chunk_stack:
189
+ if chunk_id == parent_id and parent_level == "DIEU":
190
+ # Tìm content của parent chunk này
191
+ # (Cần truyền content của parent vào đây)
192
+ break
193
+ elif level == "KHOAN" and level_value:
194
+ if not metadata.clause_number: # Chỉ điền nếu chưa có
195
+ metadata.clause_number = level_value
196
+ elif level == "DIEM" and level_value:
197
+ if not metadata.sub_clause_letter: # Chỉ điền nếu chưa có
198
+ metadata.sub_clause_letter = level_value
199
+
200
+ async def _create_context_summary_with_llm(self, content: str, metadata: ChunkMetadata) -> str:
201
+ """
202
+ Tạo context_summary bằng LLM theo format: "Structure: LEVEL | Chủ đề: Semantic: SUMMARY"
203
+ """
204
+ try:
205
+ # Tạo LEVEL từ metadata
206
+ level_parts = []
207
+ if metadata.sub_clause_letter:
208
+ level_parts.append(f"Điểm {metadata.sub_clause_letter}")
209
+ if metadata.clause_number:
210
+ level_parts.append(f"Khoản {metadata.clause_number}")
211
+ if metadata.article_number:
212
+ level_parts.append(f"Điều {metadata.article_number}")
213
+
214
+ level = " ".join(reversed(level_parts)) if level_parts else "Nội dung"
215
+
216
+ # Gọi LLM để tóm tắt chủ đề
217
+ summary_prompt = f"""
218
+ Tóm tắt ngắn gọn chủ đề chính của đoạn văn bản sau trong 1-2 câu:
219
+
220
+ {content[:500]}...
221
+
222
+ Trả về chỉ nội dung tóm tắt, không có thêm text nào khác.
223
+ """
224
+
225
+ # Sử dụng GeminiClient với RequestLimitManager
226
+ from .gemini_client import GeminiClient
227
+
228
+ gemini_client = GeminiClient()
229
+ summary_response = gemini_client.generate_text(
230
+ prompt=summary_prompt
231
+ )
232
+
233
+ summary = summary_response.strip() if summary_response else "Không có tóm tắt"
234
+
235
+ # Tạo context_summary theo format yêu cầu
236
+ context_summary = f"Structure: {level} | Chủ đề: Semantic: {summary}"
237
+
238
+ return context_summary
239
+
240
+ except Exception as e:
241
+ logger.error(f"[CHUNKER] Error creating context_summary with LLM: {e}")
242
+ # Fallback nếu LLM lỗi
243
+ level_parts = []
244
+ if metadata.sub_clause_letter:
245
+ level_parts.append(f"Điểm {metadata.sub_clause_letter}")
246
+ if metadata.clause_number:
247
+ level_parts.append(f"Khoản {metadata.clause_number}")
248
+ if metadata.article_number:
249
+ level_parts.append(f"Điều {metadata.article_number}")
250
+
251
+ level = " ".join(reversed(level_parts)) if level_parts else "Nội dung"
252
+ return f"Structure: {level} | Chủ đề: Semantic: Không có tóm tắt"
253
+
254
  def _split_into_chunks(self, text: str, chunk_size: int, overlap: int) -> List[str]:
255
  """Chia text thành các chunk với overlap."""
256
  chunks = []
 
323
  current_level_value,
324
  current_parent,
325
  vanbanid,
326
+ document_title,
327
+ chunk_stack
328
  )
329
  chunks.append(metadata)
330
 
 
356
  current_level_value,
357
  current_parent,
358
  vanbanid,
359
+ document_title,
360
+ chunk_stack
361
  )
362
  chunks.append(metadata)
363
 
 
375
  current_level_value,
376
  current_parent,
377
  vanbanid,
378
+ document_title,
379
+ chunk_stack
380
  )
381
  chunks.append(metadata)
382
 
 
420
 
421
  for i, chunk in enumerate(chunks, 1):
422
  try:
423
+ # Tạo embedding
424
+ embedding = await self.embedding_client.create_embedding(chunk.content)
425
+
426
+ # Tạo context_summary bằng LLM
427
+ context_summary = await self._create_context_summary_with_llm(chunk.content, chunk)
428
 
429
  # Chuẩn bị data cho Supabase
430
  chunk_dict = {
 
438
  'article_title': chunk.article_title,
439
  'clause_number': chunk.clause_number,
440
  'sub_clause_letter': chunk.sub_clause_letter,
441
+ 'context_summary': context_summary
442
  }
443
 
444
  # Lưu ngay lập tức vào Supabase
app/llm.py CHANGED
@@ -88,7 +88,9 @@ class LLMClient:
88
 
89
  def _setup_gemini(self, config: Dict[str, Any]):
90
  """Cấu hình cho Gemini."""
 
91
  self.gemini_client = GeminiClient()
 
92
 
93
  @timing_decorator_async
94
  async def generate_text(
 
88
 
89
  def _setup_gemini(self, config: Dict[str, Any]):
90
  """Cấu hình cho Gemini."""
91
+ # Sử dụng GeminiClient với RequestLimitManager
92
  self.gemini_client = GeminiClient()
93
+ logger.info("[LLM] Initialized GeminiClient with RequestLimitManager")
94
 
95
  @timing_decorator_async
96
  async def generate_text(
app/main.py CHANGED
@@ -369,9 +369,7 @@ async def process_business_logic(log_kwargs: Dict[str, Any], page_token: str) ->
369
  # Có thông tin phương tiện
370
  if action:
371
  logger.info(f"[DEBUG] tạo embedding: {action}")
372
- # TẠM THỜI COMMENT LẠI EMBEDDING ĐỂ TEST
373
- # embedding = await embedding_client.create_embedding(action)
374
- embedding = [0.0] * 768 # Placeholder embedding cho test
375
  logger.info(f"[DEBUG] embedding: {embedding[:5]} ... (total {len(embedding)})")
376
  matches = supabase_client.match_documents(embedding, vehicle_keywords=keywords)
377
  logger.info(f"[DEBUG] matches: {matches}")
 
369
  # Có thông tin phương tiện
370
  if action:
371
  logger.info(f"[DEBUG] tạo embedding: {action}")
372
+ embedding = await embedding_client.create_embedding(action)
 
 
373
  logger.info(f"[DEBUG] embedding: {embedding[:5]} ... (total {len(embedding)})")
374
  matches = supabase_client.match_documents(embedding, vehicle_keywords=keywords)
375
  logger.info(f"[DEBUG] matches: {matches}")
app/supabase_db.py CHANGED
@@ -89,10 +89,9 @@ class SupabaseClient:
89
  # Xử lý các giá trị null/empty cho integer fields
90
  processed_data = chunk_data.copy()
91
 
92
- # TẠM THỜI COMMENT LẠI EMBEDDING ĐỂ TEST
93
  if 'embedding' in processed_data:
94
- # processed_data['embedding'] = processed_data['embedding']
95
- del processed_data['embedding'] # Xóa embedding để test
96
 
97
  # Xử lý article_number - chỉ gửi nếu có giá trị hợp lệ
98
  if 'article_number' in processed_data:
 
89
  # Xử lý các giá trị null/empty cho integer fields
90
  processed_data = chunk_data.copy()
91
 
92
+ # Giữ lại embedding để lưu vào database
93
  if 'embedding' in processed_data:
94
+ processed_data['embedding'] = processed_data['embedding']
 
95
 
96
  # Xử lý article_number - chỉ gửi nếu có giá trị hợp lệ
97
  if 'article_number' in processed_data: