Spaces:
Runtime error
Runtime error
| """ | |
| PriceOye AI Phone Advisor β FastAPI Backend | |
| ============================================ | |
| Endpoints: | |
| POST /chat β send a message, get response + phone cards | |
| GET /phones β full phone database | |
| GET /phone/{id} β single phone detail | |
| GET /health β health check | |
| Deploy: uvicorn api.main:app --host 0.0.0.0 --port 7860 | |
| """ | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| from typing import Optional | |
| import uuid | |
| from src.conversation import ( | |
| ConversationState, handle_message, get_greeting, format_phone_summary | |
| ) | |
| from src.phone_database import build_database | |
| from src.scoring_engine import get_category_scores, get_sub_scores | |
| app = FastAPI( | |
| title="PriceOye AI Phone Advisor", | |
| description="ML-powered phone recommendation engine for Pakistan's market", | |
| version="2.0.0", | |
| ) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # In-memory session store (replace with Redis for production) | |
| SESSIONS: dict[str, ConversationState] = {} | |
| PHONE_DB = build_database() | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| # SCHEMAS | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| class ChatRequest(BaseModel): | |
| session_id: Optional[str] = None | |
| message: str | |
| class ChatResponse(BaseModel): | |
| session_id: str | |
| text: str | |
| followup: Optional[str] = None | |
| quick_replies: list[str] | |
| phones: Optional[list[dict]] = None | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| # ENDPOINTS | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| def health(): | |
| return {"status": "ok", "phones_in_db": len(PHONE_DB), "version": "2.0.0"} | |
| def chat(req: ChatRequest): | |
| # Create or retrieve session | |
| session_id = req.session_id or str(uuid.uuid4()) | |
| if session_id not in SESSIONS or req.session_id is None: | |
| # New session β return greeting | |
| greeting = get_greeting() | |
| SESSIONS[session_id] = greeting["state"] | |
| return ChatResponse( | |
| session_id=session_id, | |
| text=greeting["text"], | |
| quick_replies=greeting["quick_replies"], | |
| phones=None, | |
| ) | |
| state = SESSIONS[session_id] | |
| response = handle_message(req.message, state) | |
| SESSIONS[session_id] = response["state"] | |
| return ChatResponse( | |
| session_id=session_id, | |
| text=response["text"], | |
| followup=response.get("followup"), | |
| quick_replies=response.get("quick_replies", []), | |
| phones=response.get("phones"), | |
| ) | |
| def list_phones(): | |
| return [ | |
| { | |
| "id": p.id, | |
| "name": p.name, | |
| "brand": p.brand, | |
| "os": p.os, | |
| "price_pkr": p.price_pkr, | |
| "price_label": p.price_label, | |
| "emoji": p.emoji, | |
| "tags": p.tags, | |
| "priceoye_url": p.priceoye_url, | |
| "whatmobile_url": p.whatmobile_url, | |
| "category_scores": {k: round(v, 1) for k, v in get_category_scores(p).items()}, | |
| } | |
| for p in PHONE_DB | |
| ] | |
| def get_phone(phone_id: str, category: str = "camera"): | |
| phone = next((p for p in PHONE_DB if p.id == phone_id), None) | |
| if not phone: | |
| raise HTTPException(status_code=404, detail="Phone not found") | |
| cats = get_category_scores(phone) | |
| return { | |
| "id": phone.id, | |
| "name": phone.name, | |
| "brand": phone.brand, | |
| "os": phone.os, | |
| "price_pkr": phone.price_pkr, | |
| "price_label": phone.price_label, | |
| "emoji": phone.emoji, | |
| "tags": phone.tags, | |
| "highlights": phone.highlights, | |
| "priceoye_url": phone.priceoye_url, | |
| "whatmobile_url": phone.whatmobile_url, | |
| "category_scores": {k: round(v, 1) for k, v in cats.items()}, | |
| "deep_dive": { | |
| "category": category, | |
| "scores": get_sub_scores(phone, category), | |
| }, | |
| } | |