| """ |
| Real Estate Description Formatter API |
| Uses lightweight AI models from HuggingFace to format real estate descriptions |
| """ |
|
|
| from fastapi import FastAPI, HTTPException |
| from fastapi.middleware.cors import CORSMiddleware |
| from pydantic import BaseModel |
| from typing import Optional |
| import os |
| from huggingface_hub import InferenceClient |
|
|
| |
| app = FastAPI( |
| title="Real Estate Description Formatter", |
| description="API to format real estate descriptions with AI", |
| version="1.0.0" |
| ) |
|
|
| |
| app.add_middleware( |
| CORSMiddleware, |
| allow_origins=["*"], |
| allow_credentials=True, |
| allow_methods=["*"], |
| allow_headers=["*"], |
| ) |
|
|
| |
| |
| |
| MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-7B-Instruct") |
| HF_TOKEN = os.getenv("HF_TOKEN", None) |
|
|
| client = InferenceClient(model=MODEL_NAME, token=HF_TOKEN) |
|
|
| |
| class RealEstateInput(BaseModel): |
| description: str |
|
|
| class RealEstateOutput(BaseModel): |
| original: str |
| formatted_html: str |
| success: bool |
| error: Optional[str] = None |
|
|
| |
| SYSTEM_PROMPT = """Bạn là một chuyên gia định dạng nội dung bất động sản chuyên nghiệp. |
| |
| NHIỆM VỤ: Chuyển đổi mô tả bất động sản "xấu" (không có cấu trúc, viết tắt, thiếu dấu câu) thành HTML được format đẹp mắt với CSS inline. |
| |
| QUY TẮC QUAN TRỌNG: |
| 1. KHÔNG ĐƯỢC thay đổi nội dung text gốc - CHỈ thêm HTML tags và CSS styling |
| 2. PHẢI giữ nguyên tất cả thông tin: giá, diện tích, số phòng, địa chỉ, số điện thoại |
| 3. PHẢI phân tích và nhận diện các thành phần: |
| - Tiêu đề (tên BDS, loại nhà) |
| - Thông tin kỹ thuật (diện tích, hướng, pháp lý) |
| - Mô tả chi tiết (kết cấu, nội thất) |
| - Vị trí (địa chỉ, khu vực, tiện ích xung quanh) |
| - Giá bán |
| - Liên hệ (SĐT, tên người bán) |
| - ... các thành phần khác |
| |
| 4. SỬ DỤNG CẤU TRÚC HTML: |
| - <div class="property-card"> cho toàn bộ nội dung |
| - <h1 class="title"> cho tiêu đề chính |
| - <div class="specs"> cho thông số kỹ thuật (badge style) |
| - <div class="description"> cho mô tả chi tiết |
| - <div class="location"> cho thông tin vị trí |
| - <div class="price"> cho giá (highlight, font lớn) |
| - <div class="contact"> cho thông tin liên hệ |
| |
| 5. CSS INLINE STYLING - Màu sắc hiện đại, dễ đọc: |
| - Property card: background, border radius, shadow |
| - Title: màu đậm, font-size lớn, text-transform uppercase |
| - Specs: badge style với background color khác nhau |
| - Price: màu phù hợp, không quá chói mắt, cũng không quá trầm, nổi bật, font-weight bold, font-size lớn |
| - Icons: không sử dụng icon gì khác. |
| |
| 6. XỬ LÝ VIẾT TẮT: |
| - Giữ nguyên viết tắt nhưng thêm tooltip/title |
| - Ví dụ: <span title="Phòng ngủ">Pn</span>, <span title="Phòng vệ sinh">wc</span> |
| |
| OUTPUT FORMAT: Chỉ trả về HTML thuần, KHÔNG kèm markdown hoặc giải thích. |
| """ |
|
|
| USER_PROMPT_TEMPLATE = """Hãy format mô tả bất động sản sau thành HTML đẹp với CSS inline: |
| |
| {description} |
| |
| Nhớ: GIỮ NGUYÊN nội dung text, CHỈ thêm HTML/CSS để trình bày đẹp hơn.""" |
|
|
|
|
| @app.get("/") |
| async def root(): |
| """API root""" |
| return { |
| "name": "Real Estate Description Formatter", |
| "version": "1.0.0", |
| "model": MODEL_NAME, |
| "endpoint": "/format" |
| } |
|
|
|
|
| @app.post("/format", response_model=RealEstateOutput) |
| async def format_description(input_data: RealEstateInput): |
| """ |
| Format real estate description with AI |
| |
| Example input: |
| { |
| "description": "NHÀ 2 TẦNG HẺM OTO LIÊN HOA VĨNH NGỌC - TÂY NHA TRANG- Diện tích 92m² full ONT- Hướng Đông Bắc- Pháp lý sổ hồng hoàn công..." |
| } |
| """ |
| try: |
| |
| messages = [ |
| { |
| "role": "system", |
| "content": SYSTEM_PROMPT |
| }, |
| { |
| "role": "user", |
| "content": USER_PROMPT_TEMPLATE.format(description=input_data.description) |
| } |
| ] |
|
|
| |
| response = client.chat_completion( |
| messages=messages, |
| max_tokens=2000, |
| temperature=0.3, |
| ) |
|
|
| |
| formatted_html = response.choices[0].message.content.strip() |
|
|
| |
| if formatted_html.startswith("```html"): |
| formatted_html = formatted_html.replace("```html", "").replace("```", "").strip() |
|
|
| return RealEstateOutput( |
| original=input_data.description, |
| formatted_html=formatted_html, |
| success=True |
| ) |
|
|
| except Exception as e: |
| return RealEstateOutput( |
| original=input_data.description, |
| formatted_html="", |
| success=False, |
| error=str(e) |
| ) |
|
|
|
|
| @app.get("/health") |
| async def health_check(): |
| """Health check endpoint""" |
| return { |
| "status": "healthy", |
| "model": MODEL_NAME, |
| "service": "Real Estate Formatter" |
| } |
|
|
|
|
| if __name__ == "__main__": |
| import uvicorn |
| uvicorn.run( |
| "app:app", |
| host="0.0.0.0", |
| port=7860, |
| reload=False |
| ) |
|
|