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