feat(maturity): add dedicated NSFW/maturity analysis service

- Add maturity/ service (FastAPI + Falconsai/nsfw_image_detection ViT classifier)
  - /analyze (URL) and /analyze/file (multipart upload) endpoints
  - Normalized response: maturity_label, confidence, score, labels,
    action_hint (safe/review/flag_high), advisory, threshold_used,
    analysis_time_ms, model, source
  - Configurable thresholds via MATURITY_THRESHOLD_MATURE / MATURITY_THRESHOLD_REVIEW
  - Reuses common/image_io for URL validation and file-size enforcement
  - Explicit 502/503 errors on failure — no silent safe fallback
  - Per-request structured logging (score, label, threshold path, elapsed ms)

- Update gateway/main.py
  - MATURITY_URL + MATURITY_ENABLED env vars
  - POST /analyze/maturity and POST /analyze/maturity/file endpoints
  - /health includes maturity service status
  - _assert_maturity_enabled() guard for clean 503 when disabled
  - All existing endpoints untouched (additive change)

- Update docker-compose.yml
  - Add maturity service with healthcheck (start_period: 90s)
  - Gateway environment: MATURITY_URL, MATURITY_ENABLED
  - Gateway depends_on: maturity (service_healthy)

- Update README.md and USAGE.md
  - Document maturity service, env vars, curl examples,
    full response schema table, action_hint logic, failure guidance
This commit is contained in:
2026-04-11 17:29:26 +02:00
parent f681ab980d
commit baf497b015
7 changed files with 608 additions and 126 deletions

View File

@@ -1,10 +1,10 @@
# Skinbase Vision Stack — Usage Guide
This document explains how to run and use the Skinbase Vision Stack (Gateway + CLIP, BLIP, YOLO, Qdrant services).
This document explains how to run and use the Skinbase Vision Stack (Gateway + CLIP, BLIP, YOLO, Qdrant, Card Renderer, Maturity services).
## Overview
- Services: `gateway`, `clip`, `blip`, `yolo`, `qdrant`, `qdrant-svc`, `card-renderer` (FastAPI each, except `qdrant` which is the official Qdrant DB).
- Services: `gateway`, `clip`, `blip`, `yolo`, `qdrant`, `qdrant-svc`, `card-renderer`, `maturity` (FastAPI each, except `qdrant` which is the official Qdrant DB).
- Gateway is the public API endpoint; the other services are internal.
## Model overview
@@ -19,6 +19,8 @@ This document explains how to run and use the Skinbase Vision Stack (Gateway + C
- **Card Renderer**: Generates branded social-card images (e.g. Open Graph previews) from artwork images. Applies smart center-weighted cropping, gradient overlays, title/username/tag text, and an optional logo. Returns binary image bytes (WebP by default). Template: `nova-artwork-v1`.
- **Maturity**: Dedicated NSFW/maturity classifier. Accepts an image and returns a normalized safety signal including `maturity_label` (`safe`/`mature`), `confidence`, raw `score`, optional sublabels (e.g. `nsfw`), and an `action_hint` (`safe`, `review`, `flag_high`) designed for Nova moderation workflows. Powered by `Falconsai/nsfw_image_detection` (ViT-based, HuggingFace). Thresholds are configurable via environment variables.
## Prerequisites
- Docker Desktop (with `docker compose`) or a Docker environment.
@@ -40,6 +42,19 @@ Notes:
- `HUGGINGFACE_TOKEN` is required if the configured BLIP model requires Hugging Face authentication.
- Startup uses container healthchecks, so initial boot can take longer while models download and warm up.
Optional maturity configuration (can be added to `.env` to override defaults):
```bash
MATURITY_MODEL=Falconsai/nsfw_image_detection
MATURITY_THRESHOLD_MATURE=0.80
MATURITY_THRESHOLD_REVIEW=0.60
MATURITY_ENABLED=true
```
- `MATURITY_THRESHOLD_MATURE`: score above this → `mature` + `flag_high` (default `0.80`).
- `MATURITY_THRESHOLD_REVIEW`: score above this but below mature threshold → `mature` + `review` (default `0.60`).
- `MATURITY_ENABLED`: set to `false` to disable maturity endpoints at the gateway without removing the service.
Run from repository root:
```bash
@@ -168,9 +183,65 @@ Parameters:
Return: detected objects with `class`, `confidence`, and `bbox` (bounding box coordinates).
### Qdrant — vector storage & similarity search
### Maturity — NSFW / maturity analysis
The Qdrant integration lets you store image embeddings and find visually similar images. Embeddings are generated automatically by the CLIP service.
Analyzes an image for mature or NSFW content and returns a structured signal intended for Nova moderation workflows.
URL request:
```bash
curl -X POST https://vision.klevze.net/analyze/maturity \
-H "X-API-Key: <your-api-key>" \
-H "Content-Type: application/json" \
-d '{"url":"https://files.skinbase.org/img/aa/bb/cc/md.webp"}'
```
File upload:
```bash
curl -X POST https://vision.klevze.net/analyze/maturity/file \
-H "X-API-Key: <your-api-key>" \
-F "file=@/path/to/image.webp"
```
Example response:
```json
{
"maturity_label": "mature",
"confidence": 0.94,
"score": 0.94,
"labels": ["nsfw"],
"model": "Falconsai/nsfw_image_detection",
"threshold_used": 0.80,
"analysis_time_ms": 183.0,
"source": "maturity-service",
"action_hint": "flag_high",
"advisory": "High-confidence mature content detected"
}
```
Response fields:
| Field | Type | Description |
|---|---|---|
| `maturity_label` | string | `safe` or `mature` |
| `confidence` | float | Confidence in the label decision (01). For `safe`, this is `1 - score`. |
| `score` | float | Raw NSFW probability from the model (01). |
| `labels` | array | Sublabels when mature: currently `["nsfw"]`. Empty for safe results. |
| `model` | string | Model identifier / HuggingFace model ID. |
| `threshold_used` | float | The threshold value that determined the label. |
| `analysis_time_ms` | float | Inference time in milliseconds. |
| `source` | string | Always `maturity-service`. |
| `action_hint` | string | `safe`, `review`, or `flag_high`. Use this in Nova to drive blur/queue/flag decisions. |
| `advisory` | string | Short human-readable explanation. |
`action_hint` decision logic:
- `flag_high`: score ≥ `MATURITY_THRESHOLD_MATURE` (default 0.80) — high-confidence mature, flag for moderation.
- `review`: score ≥ `MATURITY_THRESHOLD_REVIEW` (default 0.60) but below mature threshold — possible mature, queue for human review.
- `safe`: score below both thresholds — content appears safe.
If the maturity service is unavailable the gateway returns a `502` or `503` error. **Nova must not treat a gateway failure as a `safe` result** — retry or queue for later processing. store image embeddings and find visually similar images. Embeddings are generated automatically by the CLIP service.
Qdrant point IDs must be either an unsigned integer or a UUID string. If you send another string value, the wrapper may replace it with a generated UUID and store the original value in metadata as `_original_id`.
@@ -457,7 +528,9 @@ uvicorn main:app --host 0.0.0.0 --port 8000
- Qdrant upsert error about invalid point ID: use a UUID or unsigned integer for `id`, or omit it and use the returned generated `id`.
- Image URL rejected before download: the URL may point to localhost, a private IP, a non-`http/https` scheme, or a non-image content type.
- High memory / OOM: increase host memory or reduce model footprint; consider GPUs.
- Slow startup: model weights load on service startup — expect extra time.
- Slow startup: model weights load on service startup — expect extra time. The maturity service (`start_period: 90s`) may take longer on first boot as it downloads the classifier weights (~330 MB). Mount `~/.cache/huggingface` as a volume to persist across rebuilds.
- Maturity endpoint returns `503`: `MATURITY_ENABLED` is set to `false` in environment configuration.
- Maturity endpoint returns `502`: the maturity container is unhealthy or still starting up; wait and retry.
## Extending
@@ -469,6 +542,7 @@ uvicorn main:app --host 0.0.0.0 --port 8000
- `docker-compose.yml` — composition and service definitions.
- `gateway/` — gateway FastAPI server.
- `clip/`, `blip/`, `yolo/` — service implementations and Dockerfiles.
- `maturity/` — NSFW/maturity classifier service (ViT-based, HuggingFace `Falconsai/nsfw_image_detection`).
- `qdrant/` — Qdrant API wrapper service (FastAPI).
- `card-renderer/` — card rendering service (FastAPI).
- `common/` — shared helpers (e.g., image I/O).