""" FastAPI service exposing POST /extract. Run locally: uvicorn api:app --reload --port 8000 Example: curl -X POST http://localhost:8000/extract \ -H "Content-Type: application/json" \ -d '{"text": "Lace mermaid wedding dress with long sleeves and scalloped hem"}' """ import os from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field from typing import List from predict import predict_ensemble, predict_ml from rules_extractor import extract_attributes_rules MODEL_PATH = os.path.join(os.path.dirname(__file__), "model.joblib") app = FastAPI( title="Product Attribute Extraction API", description="Converts unstructured product/fashion descriptions into structured attributes.", version="1.0.0", ) class ExtractRequest(BaseModel): text: str = Field(..., min_length=1, description="Raw product description text") mode: str = Field( "ensemble", description="Extraction mode: 'ensemble' (default, rules+ml), 'ml', or 'rules'", ) class ExtractResponse(BaseModel): text: str attributes: dict mode: str from fastapi.responses import RedirectResponse @app.get("/") def read_root(): return RedirectResponse(url="/docs") @app.get("/health") def health(): return {"status": "ok"} @app.post("/extract", response_model=ExtractResponse) def extract(req: ExtractRequest): text = req.text.strip() if not text: raise HTTPException(status_code=400, detail="text must not be empty") if req.mode == "rules": attrs = extract_attributes_rules(text) elif req.mode == "ml": attrs = predict_ml(text, model_path=MODEL_PATH) elif req.mode == "ensemble": attrs = predict_ensemble(text, model_path=MODEL_PATH) else: raise HTTPException(status_code=400, detail="mode must be one of: ensemble, ml, rules") return ExtractResponse(text=text, attributes=attrs, mode=req.mode)