Стандартный Gradio UI умер? Да здравствует gradio.Server!
За последний год я видел десятки проектов, где разработчики ломали голову над одной проблемой: как сделать красивый, современный фронтенд для своего ML-приложения на Hugging Face Spaces. Стандартный интерфейс Gradio работает, но выглядит... как стандартный интерфейс Gradio. Блоки, кнопки, предсказуемая сетка. Скучно.
До недавнего времени вариантов было два: или мириться с ограничениями, или разворачивать отдельный сервер с FastAPI и подключать его к фронтенду на React/Vue/Svelte. Второй вариант убивал всю прелесть Spaces — бесплатные GPU, простой деплой, встроенную очередь запросов.
Ключевое обновление 2025 года: команда Gradio выпустила gradio.Server — низкоуровневый API, который позволяет использовать Gradio исключительно как бэкенд. Ваш ML-код работает с очередями, автопроскалированием и всеми фичами Spaces, а фронтенд — любой, какой захотите.
Почему это меняет правила игры
Представьте: вы делаете приложение для генерации изображений с текстом внутри (как в примере Text Behind Image). Вам нужен сложный интерфейс с предпросмотром, историей генераций, сложными контролами. Стандартный Gradio справится, но будет выглядеть как учебный проект. Заказчик или пользователи ждут большего.
С gradio.Server архитектура становится простой:
- Бэкенд: Python + Gradio с вашей ML-моделью (например, Stable Diffusion 3.5)
- Фронтенд: React 19+ или Svelte 5+ с любым дизайном
- Связь: REST API или WebSockets через тот же порт
- Инфраструктура: бесплатный GPU на Spaces, очередь запросов, мониторинг
gradio.Server — следующий уровень. Вместо хаков с HTML внутри Gradio вы получаете чистую сепарацию.Реальный кейс: Text Behind Image с React-фронтендом
Давайте разберем конкретный пример — приложение, которое генерирует изображение с текстом, интегрированным в картинку (техника, популярная в соцсетях).
1 Настраиваем бэкенд на Gradio 4.15+
Первое — обновляем Gradio. На апрель 2026 актуальная версия — 4.15.2. В requirements.txt Spaces указываем:
gradio>=4.15.0
torch>=2.3.0
transformers>=4.40.0
diffusers>=0.28.0
fastapi
pydantic
Теперь пишем бэкенд. Ключевой момент — мы НЕ используем gr.Interface или gr.Blocks. Вместо этого создаем сервер:
# app.py - ML бэкенд
import gradio as gr
from gradio import Server
from pydantic import BaseModel
from typing import Optional
import torch
from diffusers import StableDiffusionPipeline
# Модель загружаем один раз при старте
pipe = StableDiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-3.5-medium",
torch_dtype=torch.float16
).to("cuda" if torch.cuda.is_available() else "cpu")
# Pydantic модель для валидации входных данных
class GenerateRequest(BaseModel):
prompt: str
negative_prompt: Optional[str] = None
steps: int = 30
guidance_scale: float = 7.5
# Функция, которая будет вызываться из фронтенда
def generate_image(request: GenerateRequest):
"""Генерация изображения с текстом внутри"""
# Здесь ваша ML-логика
# Например, модификация промпта для встраивания текста
enhanced_prompt = f"{request.prompt}, text integrated into image, typography"
result = pipe(
prompt=enhanced_prompt,
negative_prompt=request.negative_prompt,
num_inference_steps=request.steps,
guidance_scale=request.guidance_scale
)
# Возвращаем изображение в base64 или URL
image = result.images[0]
return {"image": image_to_base64(image), "status": "success"}
# Создаем сервер Gradio
server = Server(
fn=generate_image, # Основная функция
inputs=["text", "text", "number", "number"], # Типы входов для очереди
outputs=["json"], # Выход — JSON для фронтенда
title="Text Behind Image Backend",
description="ML backend for text-in-image generation",
queue=True, # Включаем очередь запросов
max_threads=2 # Ограничиваем параллельные запросы
)
# Важно: экспортируем app для Spaces
app = server.app # Это FastAPI приложение
# Добавляем health-check эндпоинт
@app.get("/health")
def health_check():
return {"status": "healthy", "model_loaded": True}
Внимание: server.app возвращает обычное FastAPI приложение. Это значит, что вы можете добавлять свои эндпоинты, middleware, авторизацию — все, что умеет FastAPI. Но не переусердствуйте: Spaces имеет ограничения на время выполнения и память.
2 Конфигурируем Hugging Face Spaces
В Spaces теперь нужно указать, что у нас кастомное приложение. В файле README.md или через UI выбираем "Docker" или "Python + Gradio". Важнее всего Dockerfile:
FROM python:3.11-slim
WORKDIR /app
# Копируем зависимости
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Копируем код
COPY . .
# Gradio по умолчанию запускается на 7860 порту
# Но наш фронтенд будет на 3000 или 5173
EXPOSE 7860
# Запускаем сервер
CMD ["python", "app.py"]
В конфигурации Spaces (файл .env или настройки UI) обязательно включаем:
GRADIO_QUEUE_ENABLED=true— для работы очередиGRADIO_ALLOWED_PATHS="./frontend/build"— если отдаем статику из папки фронтендаHF_HOME="/tmp"— для кеширования моделей на SSD
3 Создаем React-фронтенд (Next.js 15)
Теперь самое интересное — фронтенд. Создаем отдельную директорию frontend/ в репозитории Spaces. Или, что лучше, монтируем ее из другого репозитория через Git Submodules.
// frontend/src/components/ImageGenerator.jsx
import { useState } from 'react';
export default function ImageGenerator() {
const [prompt, setPrompt] = useState('');
const [loading, setLoading] = useState(false);
const [imageUrl, setImageUrl] = useState(null);
// URL вашего Spaces
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'https://your-username-spaces.hf.space';
const handleGenerate = async () => {
setLoading(true);
try {
// Вызываем эндпоинт Gradio Server
const response = await fetch(`${API_URL}/api/predict`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
data: [prompt, '', 30, 7.5] // Формат, который ждет Gradio
})
});
const result = await response.json();
// Gradio возвращает данные в data[0]
if (result.data && result.data[0]) {
const imageData = result.data[0];
// Конвертируем base64 в URL
setImageUrl(`data:image/png;base64,${imageData.image}`);
}
} catch (error) {
console.error('Generation failed:', error);
} finally {
setLoading(false);
}
};
return (
Text Behind Image Generator
);
}
Для Svelte 5 код будет еще чище — реактивность встроена в язык. Но принцип тот же: фронтенд делает POST-запрос к /api/predict эндпоинту Gradio.
А что с очередями и ZeroGPU?
Вот где проявляется магия Spaces. Когда вы запускаете gradio.Server с queue=True, все запросы автоматически попадают в распределенную очередь. Это критически важно для тяжелых ML-моделей.
| Фича | Без gradio.Server | С gradio.Server |
|---|---|---|
| Очередь запросов | Только в стандартном UI | Работает с любым фронтендом |
| ZeroGPU (автоскалирование) | Да, но для Gradio UI | Да, для вашего кастомного API |
| WebSocket соединения | Нет | Да, через server.ws_endpoint |
| Мониторинг на Spaces | Базовый | Полный (логи, метрики, алерты) |
ZeroGPU — фича, которая автоматически запускает ваш Space при запросе и останавливает после простоя. С gradio.Server она работает прозрачно: первый запрос от React-фронтенда «будит» Space, начинается генерация, результат возвращается.
Модель Context Protocol (MCP) и кастомные эндпоинты
Если ваше приложение работает с LLM (например, генерирует описания к изображениям), вам понадобится интегрировать MCP. Хорошая новость: gradio.Server отлично с этим работает.
# Добавляем MCP эндпоинт к нашему серверу
from mcp import Client
# Инициализируем MCP клиент (например, для Claude 3.7 Sonnet)
mcp_client = Client(
model="claude-3-7-sonnet-20250224",
api_key=os.getenv("ANTHROPIC_API_KEY")
)
@app.post("/api/describe")
async def describe_image(image_base64: str):
"""Используем LLM для описания изображения"""
# Ваш prompt engineering
prompt = f"""
Describe this image in detail. Focus on text elements if present.
Image data: {image_base64[:100]}...
"""
response = await mcp_client.complete(
prompt=prompt,
max_tokens=500
)
return {"description": response.text}
Теперь фронтенд может отправлять сгенерированные изображения обратно для получения текстового описания. Полный цикл ML-приложения в одном Space.
Подводные камни, которые заставят вас плакать
Я тестировал эту архитектуру на трех проектах. Вот что может пойти не так:
CORS. Чертов CORS. Spaces по умолчанию не разрешает запросы с других доменов. Решение: либо размещайте фронтенд на том же домене (через статику Spaces), либо настраивайте reverse proxy, либо используйте gr.CORS middleware в FastAPI приложении.
# Добавляем CORS middleware
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://your-frontend.vercel.app"], # Явно указываем домен
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Еще одна проблема — размеры моделей. Stable Diffusion 3.5 весит ~10GB. На бесплатном Space с 16GB RAM это на грани. Придется использовать техники оптимизации вроде quantisation или загрузки частями.
А если хочется все в одном репозитории?
Есть хак. Вы можете собрать React/Svelte фронтенд и положить статические файлы в папку, которую будет отдавать Gradio:
from fastapi.staticfiles import StaticFiles
# После создания app
app.mount("/", StaticFiles(directory="frontend/dist", html=True), name="static")
# Теперь при открытии Space показывается ваш фронтенд
# А API доступно по /api/predict
Но этот подход имеет минус: каждый раз при изменении фронтенда нужно пересобирать и пушить в репозиторий. Лучше разделить бэкенд и фронтенд на два репозитория и использовать Git Submodules.
Что дальше? Продакшн-паттерны
Когда ваш прототип заработал, пора думать о масштабировании:
- Кеширование: Добавьте Redis для хранения результатов генерации. Один и тот же промпт — одно и то же изображение.
- Асинхронные задачи: Для долгих генераций (больше 30 секунд) используйте Celery или BackgroundTasks FastAPI. Возвращайте task_id, фронтенд опрашивает статус.
- CDN для изображений: Не храните base64 в базе. Загружайте на S3 или Cloudflare R2, возвращайте URL.
- Мониторинг: Подключите Prometheus метрики к вашему
gradio.Server. Следите за временем инференса, использованием GPU, длиной очереди.
Если ваш ML-бэкенд работает с LLM, посмотрите практики контекст-инжиниринга для сложных сессий.
Итог: зачем все это?
Раньше выбор был: либо быстрый прототип на стандартном Gradio, либо месяцы разработки своей инфраструктуры. Теперь есть третий путь: ML-бэкенд на Gradio с продакшн-фичами Spaces + кастомный фронтенд на современных фреймворках.
Вы получаете бесплатные GPU, очередь, автоскалирование, мониторинг. И при этом — полный контроль над пользовательским интерфейсом. Клиент видит красивый React-сайт, а не «еще одно Gradio-приложение».
Самое интересное: эта архитектура открывает путь к гибридным моделям, где часть логики работает в браузере, часть — на сервере. Фронтенд на React может предобрабатывать данные, отправлять на ML-бэкенд, получать результаты и рендерить сложную визуализацию.
Попробуйте. Начните с простого — подключите React к уже существующему Gradio приложению. Первый запрос, первое изображение, первый восторг от того, что все работает. А потом... потом вы уже не сможете вернуться к стандартным интерфейсам.