Spaces:
Runtime error
Runtime error
Commit
·
4f7e10b
1
Parent(s):
b45d362
update: upload docs
Browse files- config.py +1 -1
- 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 |
-
|
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
|
29 |
-
#
|
|
|
|
|
|
|
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 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
|
|
|
|
|
|
|
|
|
|
49 |
else:
|
50 |
-
raise ValueError(f"
|
|
|
|
|
|
|
51 |
|
52 |
-
logger.info(f"✅
|
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 |
+
# và 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 |
|