entidi2608 commited on
Commit
4f7e10b
·
1 Parent(s): b45d362

update: upload docs

Browse files
Files changed (2) hide show
  1. config.py +1 -1
  2. services/document_service.py +94 -14
config.py CHANGED
@@ -44,7 +44,7 @@ SECRET_KEY = os.environ.get("SECRET_KEY")
44
  ALGORITHM = os.environ.get("ALGORITHM", "HS256")
45
  ACCESS_TOKEN_EXPIRE_MINUTES = os.environ.get("ACCESS_TOKEN_EXPIRE_MINUTES", 60)
46
 
47
- LLAMA_CLOUD_API_KEY=os.environ.get("LLAMA_CLOUD_API_KEY")
48
 
49
  GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID")
50
  GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET")
 
44
  ALGORITHM = os.environ.get("ALGORITHM", "HS256")
45
  ACCESS_TOKEN_EXPIRE_MINUTES = os.environ.get("ACCESS_TOKEN_EXPIRE_MINUTES", 60)
46
 
47
+ LLAMA_CLOUD_API_KEYS=os.environ.get("LLAMA_CLOUD_API_KEYS")
48
 
49
  GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID")
50
  GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET")
services/document_service.py CHANGED
@@ -18,38 +18,118 @@ logger = logging.getLogger(__name__)
18
  from rag_components import create_weaviate_schema_if_not_exists, ingest_chunks_with_native_batching
19
  from utils.process_data import hierarchical_split_law_document,extract_document_metadata,clean_document_text,infer_field, infer_entity_type, filter_and_serialize_complex_metadata
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  # --- SỬA LẠI HÀM NÀY ĐỂ NHẬN STREAM ---
22
  def convert_to_text_content(source_stream: BytesIO, original_filename: str) -> str:
23
  """Trích xuất nội dung text từ một stream trong bộ nhớ."""
24
  file_extension = Path(original_filename).suffix.lower()
25
  logger.info(f"Extracting content from: {original_filename}")
26
  content = ""
 
 
 
27
  if file_extension == ".pdf":
28
- # LlamaParse có thể cần ghi ra file tạm, chúng ta phải xử điều này
29
- # Cách 1: Ghi stream ra file tạm trong /tmp
 
 
 
30
  temp_pdf_path = f"/tmp/{original_filename}"
31
- with open(temp_pdf_path, "wb") as f:
32
- f.write(source_stream.getvalue())
33
 
34
- parser = LlamaParse(api_key=config.LLAMA_CLOUD_API_KEY, result_type="text", verbose=True, language="vi")
35
- documents = parser.load_data([temp_pdf_path])
36
- os.remove(temp_pdf_path) # Dọn dẹp ngay
37
- if documents: content = documents[0].text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  elif file_extension == ".docx":
 
40
  doc = docx.Document(source_stream)
41
  content = '\n'.join([para.text for para in doc.paragraphs])
 
42
  elif file_extension == ".doc":
43
  # pypandoc cần file trên đĩa
 
44
  temp_doc_path = f"/tmp/{original_filename}"
45
- with open(temp_doc_path, "wb") as f:
46
- f.write(source_stream.getvalue())
47
- content = pypandoc.convert_file(temp_doc_path, 'plain', format='doc')
48
- os.remove(temp_doc_path) # Dọn dẹp ngay
 
 
 
 
 
49
  else:
50
- raise ValueError(f"Unsupported file format: {file_extension}")
 
 
 
51
 
52
- logger.info(f"✅ Successfully extracted content from {original_filename}.")
53
  return content
54
 
55
 
 
18
  from rag_components import create_weaviate_schema_if_not_exists, ingest_chunks_with_native_batching
19
  from utils.process_data import hierarchical_split_law_document,extract_document_metadata,clean_document_text,infer_field, infer_entity_type, filter_and_serialize_complex_metadata
20
 
21
+ class ApiKeyManager:
22
+ """Quản lý một danh sách các API key."""
23
+ def __init__(self, api_key_string: str):
24
+ self.keys = [key.strip() for key in api_key_string.split(',') if key.strip()]
25
+ if not self.keys:
26
+ raise ValueError("Chuỗi API key không hợp lệ hoặc rỗng.")
27
+ self.current_key_index = 0
28
+ logger.info(f"Đã khởi tạo Key Manager với {len(self.keys)} key.")
29
+
30
+ def get_key(self) -> str | None:
31
+ """Trả về key hiện tại."""
32
+ if self.current_key_index < len(self.keys):
33
+ return self.keys[self.current_key_index]
34
+ return None
35
+
36
+ def get_next_key(self) -> str | None:
37
+ """Chuyển sang key tiếp theo và trả về nó."""
38
+ self.current_key_index += 1
39
+ logger.warning(f"Chuyển sang sử dụng API key tiếp theo (index: {self.current_key_index}).")
40
+ return self.get_key()
41
+
42
+ def reset(self):
43
+ """Reset lại index để bắt đầu từ key đầu tiên cho lần xử lý mới."""
44
+ self.current_key_index = 0
45
+ logger.info("Key Manager đã được reset.")
46
+
47
+ llama_key_manager = ApiKeyManager(config.LLAMA_CLOUD_API_KEYS)
48
+
49
  # --- SỬA LẠI HÀM NÀY ĐỂ NHẬN STREAM ---
50
  def convert_to_text_content(source_stream: BytesIO, original_filename: str) -> str:
51
  """Trích xuất nội dung text từ một stream trong bộ nhớ."""
52
  file_extension = Path(original_filename).suffix.lower()
53
  logger.info(f"Extracting content from: {original_filename}")
54
  content = ""
55
+
56
+ source_stream.seek(0)
57
+
58
  if file_extension == ".pdf":
59
+ # Do LlamaParse cần đường dẫn file, chúng ta sẽ ghi stream ra file tạm MỘT LẦN
60
+ # tái sử dụng đường dẫn này trong vòng lặp thử key.
61
+
62
+ # Tạo tên file tạm duy nhất để tránh xung đột khi xử lý song song
63
+
64
  temp_pdf_path = f"/tmp/{original_filename}"
 
 
65
 
66
+ try:
67
+ with open(temp_pdf_path, "wb") as f:
68
+ f.write(source_stream.getvalue())
69
+
70
+ # Reset key manager trước khi bắt đầu để đảm bảo nó luôn thử từ key đầu tiên
71
+ llama_key_manager.reset()
72
+
73
+ # Bắt đầu vòng lặp để thử các API key
74
+ while (current_key := llama_key_manager.get_key()) is not None:
75
+ try:
76
+ logger.info(f"Đang thử chuyển đổi PDF '{original_filename}' bằng key index: {llama_key_manager.current_key_index}...")
77
+ parser = LlamaParse(
78
+ api_key=current_key,
79
+ result_type="text",
80
+ verbose=True, # Giữ để debug
81
+ language="vi"
82
+ )
83
+ # Sử dụng đường dẫn file tạm đã tạo
84
+ documents = parser.load_data([temp_pdf_path])
85
+
86
+ if documents and documents[0].text.strip():
87
+ content = documents[0].text
88
+ logger.info(f"✅ Chuyển đổi PDF thành công bằng key index: {llama_key_manager.current_key_index}.")
89
+ break # Thành công, thoát khỏi vòng lặp
90
+ else:
91
+ raise ValueError("LlamaParse trả về nội dung rỗng.")
92
+
93
+ except Exception as e:
94
+ logger.error(f"❌ Lỗi với key index {llama_key_manager.current_key_index} cho file '{original_filename}': {e}")
95
+ if llama_key_manager.get_next_key() is None:
96
+ logger.critical("Đã thử hết tất cả các API key nhưng đều thất bại cho file PDF.")
97
+ raise Exception(f"Không thể chuyển đổi file '{original_filename}' sau khi đã thử tất cả các API key.") from e
98
+
99
+ if not content:
100
+ raise ValueError(f"Không thể trích xuất nội dung từ PDF '{original_filename}' sau khi thử các key.")
101
+
102
+ finally:
103
+ # Luôn dọn dẹp file tạm, dù thành công hay thất bại
104
+ if os.path.exists(temp_pdf_path):
105
+ os.remove(temp_pdf_path)
106
+ logger.debug(f"Đã dọn dẹp file tạm: {temp_pdf_path}")
107
 
108
  elif file_extension == ".docx":
109
+ # docx có thể đọc trực tiếp từ stream
110
  doc = docx.Document(source_stream)
111
  content = '\n'.join([para.text for para in doc.paragraphs])
112
+
113
  elif file_extension == ".doc":
114
  # pypandoc cần file trên đĩa
115
+
116
  temp_doc_path = f"/tmp/{original_filename}"
117
+ try:
118
+ with open(temp_doc_path, "wb") as f:
119
+ f.write(source_stream.getvalue())
120
+ content = pypandoc.convert_file(temp_doc_path, 'plain', format='doc')
121
+ finally:
122
+ if os.path.exists(temp_doc_path):
123
+ os.remove(temp_doc_path)
124
+ logger.debug(f"Đã dọn dẹp file tạm: {temp_doc_path}")
125
+
126
  else:
127
+ raise ValueError(f"Định dạng file không được hỗ trợ: {file_extension}")
128
+
129
+ if not content.strip():
130
+ raise ValueError(f"Nội dung trích xuất từ '{original_filename}' bị rỗng.")
131
 
132
+ logger.info(f"✅ Trích xuất nội dung thành công từ stream của file: {original_filename}.")
133
  return content
134
 
135