Commit 450e7acf authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

update : codebase

parent db05a5fe
...@@ -4,8 +4,9 @@ FROM python:3.11-slim ...@@ -4,8 +4,9 @@ FROM python:3.11-slim
# Thiết lập thư mục làm việc # Thiết lập thư mục làm việc
WORKDIR /app WORKDIR /app
# Cài đặt các dependencies hệ thống cần thiết (nếu có, ví dụ build tools) # Thiết lập biến môi trường để log in ra ngay lập tức
# RUN apt-get update && apt-get install -y gcc libpq-dev && rm -rf /var/lib/apt/lists/* ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# Copy requirements.txt trước để tận dụng Docker cache # Copy requirements.txt trước để tận dụng Docker cache
COPY requirements.txt . COPY requirements.txt .
...@@ -19,5 +20,5 @@ COPY . . ...@@ -19,5 +20,5 @@ COPY . .
# Expose port 5000 (Port chạy server) # Expose port 5000 (Port chạy server)
EXPOSE 5000 EXPOSE 5000
# Lệnh chạy server # Lệnh chạy server dùng uvicorn
CMD ["python", "server.py"] CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "5000"]
...@@ -168,15 +168,14 @@ def get_system_prompt() -> str: ...@@ -168,15 +168,14 @@ def get_system_prompt() -> str:
**Template:** **Template:**
``` ```
✨ **[Tên sản phẩm]** (Mã: [internal_ref_code]) 🔹 **Mã sản phẩm [thứ nhất/thứ hai/thứ ba]: [internal_ref_code] - [Tên sản phẩm]**
- Giá: [price] VNĐ • Kiểu dáng: [Mô tả thiết kế, form áo/quần]
- Màu sắc: [color1], [color2], [color3] • Chất liệu: [material_group hoặc material - mô tả đặc tính]
- Chất liệu: [material_group] • Màu sắc có sẵn: [color1], [color2], [color3]
- Mô tả: [description_text] • Giá bán: [price] VNĐ
• Đặc điểm nổi bật: [Điểm mạnh của sản phẩm, phù hợp cho ai/dịp gì]
🔗 Xem chi tiết: [product_web_url] 🔗 Xem chi tiết: [product_web_url]
📸 Ảnh: [product_image_url] 📸 Hình ảnh: [product_image_url]
→ Perfect for: [use case]
``` ```
**Quy tắc:** **Quy tắc:**
...@@ -284,7 +283,7 @@ def get_system_prompt() -> str: ...@@ -284,7 +283,7 @@ def get_system_prompt() -> str:
**Output:** **Output:**
```json ```json
{{ {{
"ai_response": "Tuyệt! Em tìm được 2 mẫu áo polo nam cực chất trong tầm giá của anh:\\n\\n✨ **Áo Polo Nam Cotton Premium** (Mã: 8TS24W001)\\n- Giá: 349,000 VNĐ\\n- Màu sắc: Đen, Trắng, Xanh navy\\n- Chất liệu: Knit - Dệt Kim\\n🔗 Xem chi tiết: https://canifa.com/ao-polo-8ts24w001\\n📸 Ảnh: https://cdn.canifa.com/8ts24w001.jpg\\n→ Chất cotton cao cấp, mặc thoải mái cả ngày!\\n\\n✨ **Áo Polo Pique Classic** (Mã: 8PL24S015)\\n- Giá: 299,000 VNĐ\\n- Màu sắc: Xám, Xanh lá\\n- Chất liệu: Cotton Pique\\n🔗 Xem chi tiết: https://canifa.com/ao-polo-8pl24s015\\n📸 Ảnh: https://cdn.canifa.com/8pl24s015.jpg\\n→ Phong cách classic, dễ phối đồ!\\n\\nAnh thích mẫu nào? Em có thể tư vấn thêm nếu anh muốn! 😊", "ai_response": "Dạ em tìm được 2 mẫu áo polo nam phù hợp với anh ạ:\\n\\n🔹 **Mã sản phẩm thứ nhất: 8TS24W001 - Áo Polo Nam Cotton Premium**\\n • Kiểu dáng: Polo cổ bẻ classic, form regular fit thoải mái\\n • Chất liệu: Knit - Dệt Kim cao cấp, mềm mại, thấm hút tốt\\n • Màu sắc có sẵn: Đen, Trắng, Xanh navy\\n • Giá bán: 349,000 VNĐ\\n • Đặc điểm nổi bật: Thiết kế basic dễ phối đồ, chất cotton cao cấp mặc thoải mái cả ngày\\n 🔗 Xem chi tiết: https://canifa.com/ao-polo-8ts24w001\\n 📸 Hình ảnh: https://cdn.canifa.com/8ts24w001.jpg\\n\\n🔹 **Mã sản phẩm thứ hai: 8PL24S015 - Áo Polo Pique Classic**\\n • Kiểu dáng: Polo classic, dáng suông thanh lịch\\n • Chất liệu: Cotton Pique có độ thoáng khí cao, bền đẹp\\n • Màu sắc có sẵn: Xám, Xanh lá\\n • Giá bán: 299,000 VNĐ\\n • Đặc điểm nổi bật: Phong cách lịch sự, phù hợp cả văn phòng và đi chơi\\n 🔗 Xem chi tiết: https://canifa.com/ao-polo-8pl24s015\\n 📸 Hình ảnh: https://cdn.canifa.com/8pl24s015.jpg\\n\\nCả 2 mẫu đều nằm trong tầm giá của anh. Anh thích kiểu nào hơn ạ? Em có thể tư vấn thêm! 😊",
"product_ids": ["8TS24W001", "8PL24S015"] "product_ids": ["8TS24W001", "8PL24S015"]
}} }}
``` ```
...@@ -337,7 +336,7 @@ def get_system_prompt() -> str: ...@@ -337,7 +336,7 @@ def get_system_prompt() -> str:
**Output:** **Output:**
```json ```json
{{ {{
"ai_response": "Ối, màu hồng fuxia đang hết hàng rồi bạn ơi 😭 Nhưng không sao, em có mẫu màu hồng siêu xinh này:\\n\\n✨ **Áo Thun Hồng Pastel** (Mã: 1TS24S089)\\n- Giá: 199,000 VNĐ\\n- Màu sắc: Hồng pastel, Hồng đào\\n- Chất liệu: Cotton 100%\\n🔗 Xem chi tiết: https://canifa.com/ao-thun-1ts24s089\\n📸 Ảnh: https://cdn.canifa.com/1ts24s089.jpg\\n→ Màu hồng nhẹ nhàng, tôn da cực kỳ!\\n\\nBạn thích không? Hoặc em có thể tìm màu khác cho bạn! 💖", "ai_response": "Ối, màu hồng fuxia đang hết hàng rồi bạn ơi 😭 Nhưng không sao, em có mẫu màu hồng siêu xinh này:\\n\\n🔹 **Mã sản phẩm: 1TS24S089 - Áo Thun Hồng Pastel**\\n • Kiểu dáng: Áo thun cổ tròn basic, form fitted vừa vặn\\n • Chất liệu: Cotton 100% mềm mại, thoáng mát\\n • Màu sắc có sẵn: Hồng pastel, Hồng đào\\n • Giá bán: 199,000 VNĐ\\n • Đặc điểm nổi bật: Màu hồng nhẹ nhàng tôn da, dễ phối đồ hàng ngày\\n 🔗 Xem chi tiết: https://canifa.com/ao-thun-1ts24s089\\n 📸 Hình ảnh: https://cdn.canifa.com/1ts24s089.jpg\\n\\nBạn thích không? Hoặc em có thể tìm màu khác cho bạn! 💖",
"product_ids": ["1TS24S089"] "product_ids": ["1TS24S089"]
}} }}
``` ```
......
import asyncio import asyncio
import platform
# Fix Windows ProactorEventLoop issue với psycopg
if platform.system() == "Windows":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
from psycopg_pool import AsyncConnectionPool from psycopg_pool import AsyncConnectionPool
......
""" import asyncio
FastAPI main application - Contract AI Service import os # Có os để mount static
import platform
Architecture:
- REST API Routes: FastAPI routers for HTTP endpoints # ==========================================
- SSE (Server-Sent Events): Real-time streaming for AI Contract and AI Legal # 🛑 QUAN TRỌNG: FIX LỖI WINDOWS Ở ĐÂY 🛑
# Phải chạy dòng này TRƯỚC KHI import bất kỳ thư viện nào khác
Modules: # ==========================================
- ai_contract: Contract generation and composition (SSE streaming) if platform.system() == "Windows":
- ai_legal: Legal Q&A and search (SSE streaming) print("🔧 Windows detected: Applying SelectorEventLoopPolicy globally...")
- conversation: Conversation history management asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
"""
# Sau khi fix xong mới import tiếp
import logging import logging
import os
# Configure Logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)
import uvicorn import uvicorn
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles # Import StaticFiles from fastapi.staticfiles import StaticFiles # Import cái này để mount HTML
# Updated APIs # Updated APIs (Import sau cùng để DB nhận cấu hình fix ở trên)
from api.chatbot_route import router as chatbot_router from api.chatbot_route import router as chatbot_router
from api.conservation_route import router as conservation_router from api.conservation_route import router as conservation_router
from config import PORT from config import PORT
# Configure Logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)
app = FastAPI( app = FastAPI(
title="Contract AI Service", title="Contract AI Service",
description="API for Contract AI Service", description="API for Contract AI Service",
version="1.0.0", version="1.0.0",
) )
# Clerk Auth (comment out if testing locally without valid token might be needed,
# but user environment has it enabled. Static files usually should be public or protected.
# For now, we mount static files BEFORE auth if we want them public, or just let auth handle it.
# Usually static files for testing are convenient to be public
# or we just need to authenticate via header in fetch.
# But for simple HTML test page, often it is easier if it is open.
# However, app.add_middleware applies to everything.
# Let's assume ClerkAuthMiddleware allows public paths or we just login.
# IMPORTANT: If ClerkAuthMiddleware blocks everything, the static page won't load easily without token.
# But user just asked to mount it.
# app.add_middleware(ClerkAuthMiddleware)
print("✅ Clerk Authentication middleware DISABLED (for testing)") print("✅ Clerk Authentication middleware DISABLED (for testing)")
# Add CORS middleware
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
allow_origins=["*"], allow_origins=["*"],
...@@ -59,23 +44,23 @@ app.add_middleware( ...@@ -59,23 +44,23 @@ app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
# ========== REST API Routes ==========
# Conversation History (Mới)
app.include_router(conservation_router) app.include_router(conservation_router)
# Chatbot Agent (Mới)
app.include_router(chatbot_router, prefix="/api/agent") app.include_router(chatbot_router, prefix="/api/agent")
# Mount this LAST to avoid conflicts with API routes # ==========================================
# 🟢 ĐOẠN MOUNT STATIC HTML CỦA BRO ĐÂY 🟢
# ==========================================
try: try:
static_dir = os.path.join(os.path.dirname(__file__), "static") static_dir = os.path.join(os.path.dirname(__file__), "static")
if not os.path.exists(static_dir): if not os.path.exists(static_dir):
os.makedirs(static_dir) os.makedirs(static_dir)
# Mount thư mục static để chạy file index.html
app.mount("/static", StaticFiles(directory=static_dir, html=True), name="static") app.mount("/static", StaticFiles(directory=static_dir, html=True), name="static")
print(f"✅ Static files mounted at /static (Dir: {static_dir})") print(f"✅ Static files mounted at /static (Dir: {static_dir})")
except Exception as e: except Exception as e:
print(f"⚠️ Failed to mount static files: {e}") print(f"⚠️ Failed to mount static files: {e}")
@app.get("/") @app.get("/")
async def root(): async def root():
return {"message": "Contract AI Service is running!", "status": "healthy"} return {"message": "Contract AI Service is running!", "status": "healthy"}
...@@ -86,20 +71,17 @@ if __name__ == "__main__": ...@@ -86,20 +71,17 @@ if __name__ == "__main__":
print("🚀 Contract AI Service Starting...") print("🚀 Contract AI Service Starting...")
print("=" * 60) print("=" * 60)
print(f"📡 REST API: http://localhost:{PORT}") print(f"📡 REST API: http://localhost:{PORT}")
print(f"📡 SSE Streaming: http://localhost:{PORT}/api/ai-contract/chat")
print(f"📡 Test Chatbot: http://localhost:{PORT}/static/index.html") print(f"📡 Test Chatbot: http://localhost:{PORT}/static/index.html")
print(f"📚 API Docs: http://localhost:{PORT}/docs") print(f"📚 API Docs: http://localhost:{PORT}/docs")
print("=" * 60) print("=" * 60)
# ENABLE_RELOAD = os.getenv("ENABLE_RELOAD", "false").lower() in ("true", "1", "yes") ENABLE_RELOAD = False
ENABLE_RELOAD = False # Tạm thời tắt reload để kiểm tra độ ổn định
print(f"⚠️ Hot reload: {ENABLE_RELOAD}") print(f"⚠️ Hot reload: {ENABLE_RELOAD}")
reload_dirs = [ reload_dirs = ["common", "api", "agent"]
"common",
"api", # Watch api folder if ENABLE_RELOAD:
"agent", # Watch agent folder os.environ["PYTHONUNBUFFERED"] = "1"
]
uvicorn.run( uvicorn.run(
"server:app", "server:app",
......
...@@ -377,9 +377,8 @@ ...@@ -377,9 +377,8 @@
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
query: text, user_query: text,
user_id: userId, user_id: userId
model_name: "gpt-4o-mini"
}) })
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment