VietCat commited on
Commit
7bc48c0
·
1 Parent(s): 2db7d31

fix log and download file

Browse files
Files changed (4) hide show
  1. app.py +19 -1
  2. rag_core/business.py +1 -1
  3. rag_core/chunker.py +77 -60
  4. rag_core/llm.py +2 -1
app.py CHANGED
@@ -3,6 +3,9 @@ from rag_core.business import answer_query, rescan_index
3
  from ui import app_ui
4
  import gradio as gr
5
  import logging
 
 
 
6
 
7
  logging.info("🚀 Khởi động ứng dụng FastAPI...")
8
 
@@ -20,7 +23,22 @@ async def rescan_api():
20
  logging.info("♻️ API /rescan được gọi")
21
  return rescan_index()
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  # Mount Gradio UI vào FastAPI tại root
24
  app = gr.mount_gradio_app(app, app_ui, path="")
25
 
26
- logging.info("✅ Gradio UI đã mount vào root /")
 
3
  from ui import app_ui
4
  import gradio as gr
5
  import logging
6
+ from fastapi.responses import JSONResponse
7
+ import json
8
+ import os
9
 
10
  logging.info("🚀 Khởi động ứng dụng FastAPI...")
11
 
 
23
  logging.info("♻️ API /rescan được gọi")
24
  return rescan_index()
25
 
26
+ @app.get("/get_structure_file")
27
+ def get_structure_file():
28
+ path = "faiss_index/chunk_structure.json"
29
+ if os.path.exists(path):
30
+ try:
31
+ with open(path, "r", encoding="utf-8") as f:
32
+ data = json.load(f)
33
+ return JSONResponse(content=data)
34
+ except Exception as e:
35
+ logging.error(f"❌ Lỗi đọc file JSON: {e}")
36
+ return {"error": f"Lỗi đọc file: {str(e)}"}
37
+ else:
38
+ logging.warning("⚠️ File chunk_structure.json không tồn tại.")
39
+ return {"error": "File không tồn tại."}
40
+
41
  # Mount Gradio UI vào FastAPI tại root
42
  app = gr.mount_gradio_app(app, app_ui, path="")
43
 
44
+ logging.info("✅ Gradio UI đã mount vào root /")
rag_core/business.py CHANGED
@@ -66,7 +66,7 @@ def answer_query(query: str) -> str:
66
 
67
  prompt = (
68
  "Bạn là một trợ lý AI có kiến thức pháp luật, hãy trả lời câu hỏi dựa trên các đoạn luật sau. "
69
- "Chỉ sử dụng thông tin có trong các đoạn, không tự đoán. Nếu không có kết quả, hãy trả lời là không có kết quả.\n"
70
  )
71
  prompt += "\n\n".join(docs)
72
  prompt += f"\n\nCâu hỏi: {query}\nTrả lời:"
 
66
 
67
  prompt = (
68
  "Bạn là một trợ lý AI có kiến thức pháp luật, hãy trả lời câu hỏi dựa trên các đoạn luật sau. "
69
+ "Chỉ sử dụng thông tin có trong các đoạn, không tự đoán.\n"
70
  )
71
  prompt += "\n\n".join(docs)
72
  prompt += f"\n\nCâu hỏi: {query}\nTrả lời:"
rag_core/chunker.py CHANGED
@@ -1,74 +1,91 @@
1
  import re
2
- import os
3
  import json
 
4
  import logging
5
- from typing import List
6
- from rag_core.utils import log_timed
7
 
8
- @log_timed("chunking văn bản luật")
9
- def chunk_legal_text(text: str) -> List[str]:
10
- # Mẫu regex cho cấp chương, điều, khoản, điểm
11
- chapter_pattern = r"(Chương\s+[IVXLC]+\s+.+?)(?=(?:\nChương\s+[IVXLC]+\s+)|\Z)"
12
- article_pattern = r"(Điều\s+\d+\..+?)(?=(?:\nĐiều\s+\d+\.|\nChương\s+[IVXLC]+|\Z))"
13
- clause_pattern = r"(?:\n|\A)(\d+\..+?)(?=(?:\n\d+\.|\n[a-zA-Z]\)|\nĐiều\s+\d+\.|\nChương\s+[IVXLC]+|\Z))"
14
- point_pattern = r"(?:\n|\A)([a-zA-Z][\)|\.].+?)(?=(?:\n[a-zA-Z][\)|\.]|\n\d+\.|\nĐiều\s+\d+\.|\nChương\s+[IVXLC]+|\Z))"
 
 
 
 
 
 
 
15
 
16
- chapters = []
17
- for chapter_match in re.finditer(chapter_pattern, text, flags=re.DOTALL):
18
- chapter_text = chapter_match.group(1).strip()
19
- chapter = {
20
- "title": chapter_text.split("\n")[0],
21
- "articles": []
22
- }
23
- article_block = chapter_text[len(chapter["title"]):].strip()
24
- for article_match in re.finditer(article_pattern, article_block, flags=re.DOTALL):
25
- article_text = article_match.group(1).strip()
26
- article = {
27
- "title": article_text.split("\n")[0],
 
 
 
28
  "clauses": []
29
  }
30
- clause_block = article_text[len(article["title"]):].strip()
31
- clauses = re.findall(clause_pattern, clause_block, flags=re.DOTALL)
32
- if clauses:
33
- for clause_text in clauses:
34
- clause = {
35
- "title": clause_text.split("\n")[0],
36
- "points": []
37
- }
38
- point_block = clause_text[len(clause["title"]):].strip()
39
- points = re.findall(point_pattern, point_block, flags=re.DOTALL)
40
- if points:
41
- clause["points"] = [p.strip() for p in points if len(p.strip()) > 10]
42
- article["clauses"].append(clause)
 
 
 
43
  else:
44
- # Nếu không clause, thêm luôn toàn bộ nội dung
45
- article["clauses"] = []
46
- chapter["articles"].append(article)
47
- chapters.append(chapter)
 
 
 
 
 
48
 
49
- # Flatten để tạo chunks
50
  chunks = []
51
- for chapter in chapters:
52
- for article in chapter.get("articles", []):
53
- if not article.get("clauses"):
54
- chunks.append(article["title"])
 
 
 
 
 
55
  continue
56
- for clause in article.get("clauses", []):
57
- if not clause.get("points"):
58
- chunks.append(clause["title"])
59
- else:
60
- for point in clause.get("points"):
61
- chunks.append(point)
62
-
63
- # Log cấu trúc nested ra file JSON
64
- json_path_local = "faiss_index/chunk_structure.json"
65
- os.makedirs(os.path.dirname(json_path_local), exist_ok=True)
66
- with open(json_path_local, "w", encoding="utf-8") as f:
67
- json.dump(chapters, f, indent=2, ensure_ascii=False)
68
- logging.info(f"✅ Đã ghi cấu trúc nested vào {json_path_local}")
69
 
70
- # Gợi ý đường dẫn tải cho Hugging Face Spaces
71
- download_link = "/file/faiss_index/chunk_structure.json"
72
- logging.info(f"📎 Link tải JSON cấu trúc: {download_link}")
 
 
 
 
 
 
 
 
73
 
74
  return chunks
 
1
  import re
 
2
  import json
3
+ import os
4
  import logging
 
 
5
 
6
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
7
+
8
+ SECTION_RE = re.compile(r"^\s*(Điều\s+\d+[A-Z]?)\.?\s*(.*)")
9
+ CLAUSE_RE = re.compile(r"^\s*(\d+)\.?\s+(.*)")
10
+ POINT_RE = re.compile(r"^\s*([a-zA-Z])\)\s+(.*)")
11
+
12
+ PUBLIC_CHUNK_JSON_PATH = "faiss_index/chunk_structure.json"
13
+
14
+
15
+ def chunk_legal_text(text):
16
+ logging.info("📑 Bắt đầu chunk văn bản luật...")
17
+ articles = []
18
+ current_article = None
19
+ current_clause = None
20
 
21
+ for line in text.splitlines():
22
+ line = line.strip()
23
+ if not line:
24
+ continue
25
+
26
+ sec_match = SECTION_RE.match(line)
27
+ clause_match = CLAUSE_RE.match(line)
28
+ point_match = POINT_RE.match(line)
29
+
30
+ if sec_match:
31
+ if current_article:
32
+ articles.append(current_article)
33
+ current_article = {
34
+ "article": sec_match.group(1),
35
+ "title": sec_match.group(2),
36
  "clauses": []
37
  }
38
+ current_clause = None
39
+ elif clause_match and current_article:
40
+ current_clause = {
41
+ "clause": clause_match.group(1),
42
+ "text": clause_match.group(2),
43
+ "points": []
44
+ }
45
+ current_article["clauses"].append(current_clause)
46
+ elif point_match and current_clause:
47
+ current_clause["points"].append({
48
+ "point": point_match.group(1),
49
+ "text": point_match.group(2)
50
+ })
51
+ elif current_clause:
52
+ if current_clause["points"]:
53
+ current_clause["points"][-1]["text"] += " " + line
54
  else:
55
+ current_clause["text"] += " " + line
56
+ elif current_article:
57
+ if current_article["clauses"]:
58
+ current_article["clauses"][-1]["text"] += " " + line
59
+
60
+ if current_article:
61
+ articles.append(current_article)
62
+
63
+ logging.info(f"🔎 Đã phân tích được {len(articles)} điều luật")
64
 
 
65
  chunks = []
66
+ for article in articles:
67
+ article_header = f"{article['article']}. {article['title']}"
68
+ if not article.get("clauses"):
69
+ chunks.append(article_header)
70
+ continue
71
+ for clause in article.get("clauses", []):
72
+ clause_header = f"{article['article']}.{clause['clause']}: {clause['text']}"
73
+ if not clause.get("points"):
74
+ chunks.append(f"{article_header}\n{clause_header}")
75
  continue
76
+ for point in clause.get("points", []):
77
+ chunks.append(f"{article_header}\n{clause_header}\n{point['point']}) {point['text']}")
 
 
 
 
 
 
 
 
 
 
 
78
 
79
+ try:
80
+ os.makedirs(os.path.dirname(PUBLIC_CHUNK_JSON_PATH), exist_ok=True)
81
+ with open(PUBLIC_CHUNK_JSON_PATH, "w", encoding="utf-8") as f:
82
+ json.dump(articles, f, ensure_ascii=False, indent=2)
83
+ logging.info(f"✅ Đã ghi cấu trúc nested vào {PUBLIC_CHUNK_JSON_PATH}")
84
+ if os.path.exists(PUBLIC_CHUNK_JSON_PATH):
85
+ logging.info("📁 File chunk_structure.json đã được tạo thành công và có thể truy cập công khai.")
86
+ else:
87
+ logging.warning("⚠️ File chunk_structure.json không tồn tại sau khi ghi.")
88
+ except Exception as e:
89
+ logging.error(f"❌ Lỗi khi ghi file JSON: {e}")
90
 
91
  return chunks
rag_core/llm.py CHANGED
@@ -2,7 +2,7 @@ import requests
2
  import logging
3
  import time
4
 
5
- LLM_ENDPOINT = "https://vietcat-gemma34b.hf.space/analyze"
6
 
7
  def generate_answer(prompt: str) -> str:
8
  max_retries = 3
@@ -10,6 +10,7 @@ def generate_answer(prompt: str) -> str:
10
 
11
  for attempt in range(1, max_retries + 1):
12
  try:
 
13
  logging.info(f"📡 Gửi request đến LLM (lần {attempt}, timeout={timeout}s)...")
14
  response = requests.post(
15
  LLM_ENDPOINT,
 
2
  import logging
3
  import time
4
 
5
+ LLM_ENDPOINT = "https://vietcat-gemma34b.hf.space/purechat"
6
 
7
  def generate_answer(prompt: str) -> str:
8
  max_retries = 3
 
10
 
11
  for attempt in range(1, max_retries + 1):
12
  try:
13
+ logging.info(f"📡 Gửi request đến LLM tại {LLM_ENDPOINT}")
14
  logging.info(f"📡 Gửi request đến LLM (lần {attempt}, timeout={timeout}s)...")
15
  response = requests.post(
16
  LLM_ENDPOINT,