Spaces:
Build error
Build error
| import gradio as gr | |
| import pandas as pd | |
| from datetime import datetime | |
| import json | |
| import io | |
| from PIL import Image | |
| import pytesseract | |
| from huggingface_hub import hf_hub_download # Thư viện mới | |
| from llama_cpp import Llama # Thư viện mới | |
| # --- CẤU HÌNH VÀ TẢI MÔ HÌNH (SỬ DỤNG LLAMA.CPP) --- | |
| print("Ứng dụng đang khởi động...") | |
| # OCR không thay đổi | |
| print("Sử dụng Tesseract OCR (siêu nhẹ).") | |
| # THAY ĐỔI LỚN: DÙNG LLAMA-CPP-PYTHON | |
| print("Đang tải mô hình LLM (Llama-3-8B GGUF for CPU)...") | |
| model_repo = "bartowski/Meta-Llama-3-8B-Instruct-GGUF" | |
| model_file = "Meta-Llama-3-8B-Instruct-Q4_K_M.gguf" | |
| # Tải file mô hình về cache của Space | |
| model_path = hf_hub_download(repo_id=model_repo, filename=model_file) | |
| print(f"Đã tải xong file mô hình tại: {model_path}") | |
| # Khởi tạo mô hình từ file đã tải | |
| llm = Llama( | |
| model_path=model_path, | |
| n_ctx=4096, # Context length | |
| n_gpu_layers=0, # Chạy hoàn toàn trên CPU | |
| verbose=True, # In ra thông tin để debug | |
| ) | |
| print("Tải xong và khởi tạo thành công mô hình LLM.") | |
| # --- CÁC HÀM XỬ LÝ --- | |
| def run_ocr(image: Image.Image) -> str: | |
| # (Giữ nguyên) | |
| try: | |
| text = pytesseract.image_to_string(image) | |
| return text | |
| except Exception as e: | |
| print(f"Lỗi Tesseract: {e}") | |
| return "Lỗi khi đọc chữ từ ảnh." | |
| def extract_order_from_text(text: str) -> dict: | |
| # Cập nhật prompt và cách gọi cho Llama.cpp | |
| prompt = f"""<|begin_of_text|><|start_header_id|>system<|end_header_id|> | |
| You are an expert assistant that only outputs valid JSON. Extract order information from the text. The JSON object must contain "ten_khach_hang" (string, null if not found) and "danh_sach_hang" (an array of items). Each item must have "ten_hang" (string), "so_luong" (number), "don_vi" (string), "ma_hang" (string, null if not found), and "ghi_chu" (string, null if not found).<|eot_id|><|start_header_id|>user<|end_header_id|> | |
| Text Content: | |
| {text}<|eot_id|><|start_header_id|>assistant<|end_header_id|> | |
| """ | |
| output = llm( | |
| prompt, | |
| max_tokens=1024, | |
| stop=["<|eot_id|>"], | |
| temperature=0.1, | |
| echo=False # Không in lại prompt trong kết quả | |
| ) | |
| response_text = output['choices'][0]['text'] | |
| try: | |
| json_str = response_text.strip() | |
| start = json_str.find('{') | |
| end = json_str.rfind('}') + 1 | |
| if start != -1 and end != 0: | |
| json_str = json_str[start:end] | |
| return json.loads(json_str) | |
| except json.JSONDecodeError: | |
| return {"error": "AI trả về định dạng không hợp lệ", "raw_response": response_text} | |
| def create_excel_file(order_data: dict): | |
| # (Giữ nguyên) | |
| if not order_data or "danh_sach_hang" not in order_data or not order_data["danh_sach_hang"]: return None | |
| flat_data = [] | |
| customer = order_data.get('ten_khach_hang', 'N/A') | |
| for item in order_data['danh_sach_hang']: | |
| flat_data.append({ | |
| 'Khách hàng': customer, 'Mã hàng': item.get('ma_hang'), | |
| 'Tên hàng': item.get('ten_hang'), 'Số lượng': item.get('so_luong'), | |
| 'Đơn vị': item.get('don_vi'), 'Ghi chú': item.get('ghi_chu') | |
| }) | |
| df = pd.DataFrame(flat_data) | |
| output = io.BytesIO() | |
| with pd.ExcelWriter(output, engine='openpyxl') as writer: df.to_excel(writer, index=False, sheet_name='DonHang') | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| filename = f"don_hang_{timestamp}.xlsx" | |
| return (filename, output.getvalue()) | |
| def process_image_and_extract(image): | |
| # (Giữ nguyên) | |
| try: | |
| if image is None: return "Vui lòng dán ảnh vào.", None, None | |
| extracted_text = run_ocr(image) | |
| if not extracted_text.strip(): return "Không đọc được chữ từ hình ảnh.", None, None | |
| order_data = extract_order_from_text(extracted_text) | |
| if "error" in order_data: return extracted_text, f"Lỗi từ AI: {order_data['error']}\nPhản hồi gốc: {order_data['raw_response']}", None | |
| excel_info = create_excel_file(order_data) | |
| df_display = pd.DataFrame(order_data.get('danh_sach_hang', [])) | |
| if excel_info: | |
| filename, filebytes = excel_info | |
| with open(filename, "wb") as f: f.write(filebytes) | |
| return extracted_text, df_display, filename | |
| else: return extracted_text, df_display, None | |
| except Exception as e: | |
| import traceback | |
| error_str = str(e) | |
| traceback_str = traceback.format_exc() | |
| print(traceback_str) | |
| return f"Lỗi nghiêm trọng: {error_str}", None, None | |
| # --- XÂY DỰNG GIAO DIỆN GRADIO (GIỮ NGUYÊN) --- | |
| with gr.Blocks(theme=gr.themes.Soft()) as app: | |
| gr.Markdown("# Ứng dụng Trích xuất Đơn hàng từ Ảnh chụp màn hình") | |
| gr.Markdown("Chụp màn hình email/tin nhắn đặt hàng, sau đó dán (Ctrl+V) vào ô bên dưới và nhấn 'Xử lý'.") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| image_input = gr.Image(label="Dán ảnh chụp màn hình vào đây", type="pil", sources=["clipboard", "upload"]) | |
| process_btn = gr.Button("Xử lý", variant="primary") | |
| with gr.Column(scale=2): | |
| gr.Markdown("### Kết quả trích xuất") | |
| output_table = gr.DataFrame(label="Chi tiết đơn hàng") | |
| output_excel = gr.File(label="Tải file Excel") | |
| gr.Markdown("### Văn bản đọc được từ ảnh (OCR)") | |
| output_text = gr.Textbox(label="Text from Image", lines=10, interactive=False) | |
| process_btn.click(fn=process_image_and_extract, inputs=image_input, outputs=[output_text, output_table, output_excel]) | |
| app.launch(debug=True) |