Spaces:
Sleeping
Sleeping
File size: 2,289 Bytes
896a559 af3f5bb 896a559 af3f5bb 896a559 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | """
Root-level FastAPI entrypoint (Hugging Face Spaces convention expects an
app.py at the repo root defining `app`).
This is a thin wrapper: the actual extraction logic lives in src/ (rules
extractor, trained ML model, ensemble predictor) so the core pipeline stays
testable and importable on its own; this file just wires it up as the
Space's entrypoint and defines the HTTP endpoints.
Run locally from the repo root:
uvicorn app:app --reload --port 8000
On Hugging Face Docker Spaces, the Dockerfile's CMD runs this same module
on port 7860.
"""
import os
import sys
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
SRC_DIR = os.path.join(BASE_DIR, "src")
sys.path.insert(0, SRC_DIR)
MODEL_PATH = os.path.join(SRC_DIR, "model.joblib")
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from rules_extractor import extract_attributes_rules
from predict import predict_ensemble, predict_ml
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 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)
|