DEV Community

Saad Sellami
Saad Sellami

Posted on

How we built an AI Fashion Rater

Fashion feedback is broken. Friends are too polite. Social media is intimidating. Stylists are expensive.
We built OutfitScore to fix that — an AI that scores outfits, makeup, and accessories on a 0–100 scale with honest, structured feedback. Here's the quick technical story.

The Stack

  • Backend: FastAPI on Railway
  • Frontend: Alpine.js + Jinja2 + Tailwind CSS
  • Cache: Redis + PostgreSQL
  • Payments: Paddle Billing

How an Analysis Works

1. Duplicate check first. Before any AI call, we hash the image and check Redis. Cache keys are context-aware — the same photo analyzed for "casual" vs. "formal" gets separate entries since the feedback differs. This alone cut redundant AI calls dramatically.

2. Direct-to-Cloudinary upload. The file never touches our server. We generate a signed upload config, the client uploads directly via XHR, and Cloudinary fires a callback we validate against a Redis session token.

3. Gemini does the scoring. Prompt engineering turned out to be the hardest part. Two rules that made a real difference:

  • Score last. We explicitly instruct the model to reason about fit, color, and occasion before committing to a number. Scores became far more consistent.
  • Handle dark clothing. The model kept misreading dark outfits as "poor fit." A single line in the prompt fixed it: "Look for texture and lapels — don't mistake dark lighting for shapelessness."

We use a strict scoring legend (90–100 = Excellent, 50–69 = Fair, 0–49 = Poor) to prevent grade inflation. Honest scores are the product.

4. Results via WebSocket. The frontend opens a WebSocket when analysis starts. If that fails (firewalls, mobile networks), it silently falls back to HTTP polling. Users never see an error.

5. Affiliate products load async. Product recommendations via Sovrn Commerce load after the score appears — users aren't waiting on affiliate calls.


Lessons Learned

Geo-targeting is subtle. Sovrn was returning Dutch products for everyone because our Railway server is in Europe. The API used the server's IP for geo-detection. Passing the client's IP in the request header fixed it immediately.

Honest scores build trust. We could give everyone 80+. Instead we give 40s when outfits deserve it — and users come back specifically because they believe us.


OutfitScore is live at OutfitScore. Happy to answer questions in the comments.

Top comments (0)