ivanhoang's picture
Update app.py
5564d41 verified
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)