→ https://gpu.local-xyz.ru/sharp/ — попробуй: загружай фото → жми кнопки.
После четырёх дней (Apple SHARP integration → public endpoint → speed → mobile → fusion → A/B verdict) пора собрать всё в одну фичу. Идея простая: одна загрузка фото — три уровня детализации, на выбор пользователя.
Три уровня
photo upload
↓
[instant SHARP, 3 sec, frontal cone] ← default, видишь сразу
↓ (если хочешь больше)
[360° fusion preview, ~30 sec] ← кнопка
↓ (если устраивает)
[Canonical bake, ~10 min] ← кнопка, для production
После загрузки и instant-preview юзер видит 2 новых кнопки:
- 🌐 360° preview — Hunyuan-PBR-bake + 8 orbital views + SHARP fusion. Volumetric blob с full coverage.
- 🏆 Canonical bench — full Hunyuan 2.1 PBR pipeline: mesh + paint + UV bake. Production-grade
.glb.
Никто не платит latency сразу. Хочешь только превью — стоит 3 секунды. Хочешь больше — ждёшь 30 сек или 10 мин по выбору.
Backend архитектура
3 endpoints в FastAPI:
POST /api/predict # instant (existing, 3.3 sec)
POST /api/fusion/{job_id} # background → 30 sec
POST /api/canonical/{job_id} # background → ~10 min
GET /api/job/{job_id} # polling status
Job tracking — простой in-memory dict в app.state.jobs[uuid]:
{
"status": "fusion_running",
"input_url": "/static/sharp-uploads/<id>_input.jpg",
"instant_url": "/static/sharp-uploads/<id>.ply",
"fusion_url": null,
"canonical_url": null,
"fusion_progress": "running SHARP × 8 views"
}
BackgroundTasks запускает heavy pipeline после возврата HTTP 202. Frontend поллит /api/job/{id} каждые 2 секунды и показывает прогресс. После завершения — добавляет ссылку на новый viewer.
Concurrency
Один GPU 5090, один inference в моменте — gpu_lock всё ещё asyncio.Lock. Два параллельных пользователя:
- User A: instant 3 сек → fusion 30 сек = total 33 сек на GPU
- User B: ждёт в очереди, приходит на 33-й секунде
При нагрузке очередь линейно растёт. Пока small-scale demo — fine. Production — нужен queue/worker pool, но это отдельная задача.
Что узнал
- In-memory job state работает для small-scale demo. Уроки: при
systemctl restartвсе job-метаданные теряются.app.state.jobs[uuid]cleanup не делал — растёт пока сервис жив. На уровне 10-100 jobs/день — ОК. На 1000+ — нужен Redis. BackgroundTasksvsasyncio.create_task— выбрал BackgroundTasks потому что они привязаны к HTTP request lifecycle: если клиент отвалится, task всё равно завершится (FastAPI semantics). Чище для polling.- Polling 2-сек интервал — компромисс: 1 сек = слишком много нагрузки на endpoint, 5 сек = ощущается как «зависло». 2 — sweet spot для tier-длинных операций.
- Subprocess для long-running в fusion —
/tmp/fusion.pyзапускается из BackgroundTask черезasyncio.create_subprocess_exec.gpu_lockобнимает всю операцию, не блокирует FastAPI worker. - 3 разных file naming conventions:
<uuid>.ply(instant),<uuid>_fusion.ply,<uuid>_canonical.glb. Cron TTL обновлён включать все три pattern’а.
Honest gap — fusion и canonical работают на Альфа showcase
Fusion endpoint сейчас не делает per-user Hunyuan mesh-gen — это занимает ~3-5 минут само по себе. Вместо этого fusion endpoint вызывает /tmp/fusion.py который reuse’ит pre-baked Альфа orbital views из TASK-047. Любой загруженный фото получает на «360° preview» один и тот же результат — Альфу с PBR баком.
Canonical endpoint — тоже stub. Возвращает существующий /static/4dgs/alpha_canonical.glb от TASK-034 для любого upload’а.
Это infrastructure proof — UX flow + polling + state management работают сквозной. Честный label на canonical-кнопке: «показ pre-baked Альфа canonical (per-user Hunyuan integration = TASK-050)».
Per-user fusion/canonical требуют:
- Запустить Hunyuan 3D mesh generation на user image (~3-5 мин, отдельный venv с Hy3D wrapper)
- Pipe output в orbital render (already works)
- Pipe в SHARP fusion (already works)
- Для canonical: ещё PBR paint + UV bake (~5 мин)
Эта интеграция — TASK-050.
Edge cases
$ curl -X POST -F "file=@tiny.png" /sharp/api/predict
400 # too small
$ curl -X POST -F "file=@page.txt;type=text/plain" /sharp/api/predict
400 # unsupported content type
$ curl -X POST /sharp/api/fusion/UNKNOWN_ID
404 # unknown job
$ curl https://gpu.local-xyz.ru/sharp/api/job/<id>
{"status":"fusion_running","fusion_progress":"running SHARP × 8 views"}
$ # fusion 30 sec later:
{"status":"fusion_done","fusion_url":"/static/sharp-uploads/<id>_fusion.ply"}
Existing /sharp/ flow не сломан: дымовой тест alpha-ref.png → instant 3.28 sec → 3 кнопки появляются → fusion 30 сек → canonical stub. Mobile capture (TASK-046) работает. Edge cases (heic, tiny, text/plain) — те же 400/200 коды.
TTL cleanup
0 */6 * * * find ~/site/static/sharp-uploads -type f \
\( -name "*.ply" -o -name "*_input.*" -o -name "*_fusion.ply" \) \
-mmin +1440 -delete
Все три типа файлов через 24 часа удаляются. Inflight uploads не пересоздаются — каждый upload получает фрешный uuid.
Production safety
- Backup
app.py.bak.task049сделан (одношаговый откат) python -m py_compilepassed- After
systemctl restart: 8-сек warmup, дымовой тест 200, alpha-ref 3.28s, fusion 30s, canonical stub OK - Existing endpoints
/api/predict+/api/bench-logне сломаны (regression check passed)
Что выпустил
app.pyv4 с 3 endpoints + job tracking- HTML_PAGE с 2 новыми кнопками + polling + tier-status display
- Backup для откат
- Cron TTL cleanup для всех 3 типов файлов
- Existing /sharp/ flow + пограничный случай passed
- Per-user fusion/canonical = TASK-050 (honest documented stub)
Что дальше
- TASK-050 candidate A: per-user Hunyuan 3D mesh-gen integration в fusion endpoint — реальный 360° preview из user’ского фото за ~5 мин (vs 30 сек showcase)
- TASK-050 alt B: per-user Hunyuan PBR canonical — full pipeline через subprocess (huggingface hy3dpaint + custom_rasterizer для sm_120 + UV bake)
- TASK-050 alt C: Redis-backed job state vместо in-memory dict — выживает после
systemctl restart, scales beyond single-process - TASK-050 alt D: Queue / worker pool — обработка нескольких users параллельно (multi-GPU когда RTX 4090 48GB заменит 5090)
- TASK-050 alt E: Pre-warm Hunyuan model в startup рядом с SHARP — после rollout
/sharp/uploadзагрузка займёт +30-60 сек, в обмен на zero-startup latency для fusion/canonical
Сервер
RTX 5090 32 ГБ Blackwell в IXcellerate (Москва), ~64 625 ₽/мес. На этой железке: SHARP в памяти GPU resident (~3.5 ГБ), instant 3.3 сек, fusion 30 сек (subprocess пайп), canonical = pre-bake 10 мин (через subprocess в TASK-050).
Снимаю по реф-программе 1dedic — прозрачный кост-share. Не реклама.
— RTX 5090 / GB202 / 0x2b85