| import os |
| import re |
| import requests |
| from fastapi import FastAPI |
| from pydantic import BaseModel |
| from typing import List, Optional |
| from fastapi.middleware.cors import CORSMiddleware |
| from huggingface_hub import InferenceClient |
|
|
| app = FastAPI(title="Yuyu - LibraryLuxe AI") |
|
|
| app.add_middleware( |
| CORSMiddleware, |
| allow_origins=["*"], |
| allow_credentials=True, |
| allow_methods=["*"], |
| allow_headers=["*"], |
| ) |
|
|
| |
| HF_TOKEN = os.getenv("HF_TOKEN") |
| client = InferenceClient(api_key=HF_TOKEN) |
|
|
| |
| MODEL = "Qwen/Qwen2.5-7B-Instruct" |
| |
| |
|
|
| |
|
|
| class ChatHistoryItem(BaseModel): |
| role: str |
| content: str |
|
|
| class ChatRequest(BaseModel): |
| message: str |
| history: List[ChatHistoryItem] = [] |
|
|
| class BookEnrichment(BaseModel): |
| title: Optional[str] = None |
| author: Optional[str] = None |
| cover_url: Optional[str] = None |
| description: Optional[str] = None |
| isbn: Optional[str] = None |
|
|
| class ChatResponse(BaseModel): |
| response: str |
| book_data: Optional[BookEnrichment] = None |
|
|
| |
| SYSTEM_PROMPT = """ |
| You are Yuyu, the Master Librarian at LibraryLuxe. |
| ### CORE RULES: |
| - ALL prices and balances must be mentioned in MMK (Myanmar Kyat). |
| - Use the 'Actual Library Plans' and 'User Context' provided in the message for precision. |
| ### 1. MEMBERSHIP & SUBSCRIPTIONS |
| - We have different plans (Basic, Premium, Basic yearly, Premium yearly.) with different book limits and prices. |
| (Basic: maxed books=3, Borrow days=14 days, Premium: maxed books=5, Borrow days=21days) |
| - Users can subscribe using their Wallet or other payment methods. |
| - IMPORTANT: When a user subscribes, it might stay "Pending" until an Admin approves it. |
| - To Renew/Upgrade: Go to the 'Membership' page and click 'Renew' or 'Upgrade'. |
| ### 2. WALLET & PAYMENTS |
| - Users have a digital Wallet. They can 'Top Up' money into it. |
| - Wallet balance can be used to buy subscriptions instantly. |
| - To Top Up: Go to the 'Wallet' section in the profile menu. |
| ### 3. LOYALTY & REWARDS |
| - Users earn 'Loyalty Points' for borrowing books and buying membership plans. |
| - Points can be redeemed for 'Rewards' in 'Rewards' page. |
| - To check points: Visit the 'Rewards' page. |
| ### 4. BORROWING & RETURNS |
| - Each plan has a 'Max Books' limit and 'Borrowing Days' limit. |
| - To Return: Users must 'Request Return' from their 'My Books' page OR go to the library and return it. A librarian will then verify the physical return. |
| - Overdue books might prevent new borrowings. |
| ### 5. PERSONALITY & TONE |
| - Be warm, helpful, and professional. |
| - Use the user's name if provided. |
| - ALWAYS use the 'Actual Library Plans' and 'User Context' provided in the message for precision. |
| - If you don't know a specific detail about their account, ask them to check their 'Dashboard' or email aiyurina1610@gmail.com. |
| Keep responses helpful but concise (max 2 paragraphs). |
| """ |
|
|
|
|
|
|
| def get_book_details(isbn: str) -> Optional[BookEnrichment]: |
| try: |
| url = f"https://openlibrary.org/api/books?bibkeys=ISBN:{isbn}&format=json&jscmd=data" |
| resp = requests.get(url, timeout=6) |
| data = resp.json() |
| key = f"ISBN:{isbn}" |
| if key in data: |
| b = data[key] |
| return BookEnrichment( |
| title=b.get("title"), |
| author=", ".join([a.get("name", "") for a in b.get("authors", [])]), |
| cover_url=b.get("cover", {}).get("large"), |
| description=b.get("notes") or b.get("description"), |
| isbn=isbn |
| ) |
| except: |
| pass |
| return None |
|
|
| @app.get("/") |
| def home(): |
| return {"status": "online", "message": "Yuyu is ready!"} |
|
|
| @app.post("/chat", response_model=ChatResponse) |
| async def chat(request: ChatRequest): |
| |
| isbn_match = re.search(r"\b(?:97[89])?\d{9}[\dX]\b", request.message) |
| book_data = get_book_details(isbn_match.group(0)) if isbn_match else None |
|
|
| |
| messages = [{"role": "system", "content": SYSTEM_PROMPT}] |
| |
| |
| for item in request.history[-6:]: |
| messages.append({"role": item.role, "content": item.content}) |
| |
| messages.append({"role": "user", "content": request.message}) |
|
|
| try: |
| response = client.chat.completions.create( |
| model=MODEL, |
| messages=messages, |
| max_tokens=450, |
| temperature=0.75, |
| top_p=0.9, |
| ) |
| ai_reply = response.choices[0].message.content.strip() |
| |
| except Exception as e: |
| ai_reply = "I'm a bit overloaded right now... Please try again in a few seconds." |
|
|
| return ChatResponse(response=ai_reply, book_data=book_data) |