Gradio Server + React/Svelte: Кастомный фронтенд для ML на Hugging Face Spaces | AiManual
AiManual Logo Ai / Manual.
12 Апр 2026 Гайд

Gradio Server: ML-бэкенд на Hugging Face Spaces теперь совместим с любым фронтендом

Подробный гайд по подключению React или Svelte к ML-бэкенду через gradio.Server API. Очереди, ZeroGPU и FastAPI-расширение в 2026 году.

Стандартный 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, очередь запросов, мониторинг
💡
Если вы уже работали с gr.HTML для кастомных интерфейсов, то 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.

Что дальше? Продакшн-паттерны

Когда ваш прототип заработал, пора думать о масштабировании:

  1. Кеширование: Добавьте Redis для хранения результатов генерации. Один и тот же промпт — одно и то же изображение.
  2. Асинхронные задачи: Для долгих генераций (больше 30 секунд) используйте Celery или BackgroundTasks FastAPI. Возвращайте task_id, фронтенд опрашивает статус.
  3. CDN для изображений: Не храните base64 в базе. Загружайте на S3 или Cloudflare R2, возвращайте URL.
  4. Мониторинг: Подключите Prometheus метрики к вашему gradio.Server. Следите за временем инференса, использованием GPU, длиной очереди.

Если ваш ML-бэкенд работает с LLM, посмотрите практики контекст-инжиниринга для сложных сессий.

Итог: зачем все это?

Раньше выбор был: либо быстрый прототип на стандартном Gradio, либо месяцы разработки своей инфраструктуры. Теперь есть третий путь: ML-бэкенд на Gradio с продакшн-фичами Spaces + кастомный фронтенд на современных фреймворках.

Вы получаете бесплатные GPU, очередь, автоскалирование, мониторинг. И при этом — полный контроль над пользовательским интерфейсом. Клиент видит красивый React-сайт, а не «еще одно Gradio-приложение».

Самое интересное: эта архитектура открывает путь к гибридным моделям, где часть логики работает в браузере, часть — на сервере. Фронтенд на React может предобрабатывать данные, отправлять на ML-бэкенд, получать результаты и рендерить сложную визуализацию.

Попробуйте. Начните с простого — подключите React к уже существующему Gradio приложению. Первый запрос, первое изображение, первый восторг от того, что все работает. А потом... потом вы уже не сможете вернуться к стандартным интерфейсам.

Подписаться на канал