Heli + FastAPI: backend для LLM со стримингом

Правильная схема для веб- и мобильных приложений: фронтенд → ваш FastAPI → Heli API. Ключ sk-heli- хранится только в переменных окружения сервера и никогда не попадает в браузер. FastAPI асинхронный, поэтому используйте AsyncOpenAI: синхронный клиент внутри async-обработчика блокирует event loop, и сервер перестаёт отвечать другим запросам, пока модель генерирует ответ.

Маршрут со стримингом

Проверка chunk.choices обязательна: Heli включает в поток итоговый чанк с данными usage, а у него список choices пустой. Без проверки код упадёт с IndexError на последнем чанке.

python
import os
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from openai import AsyncOpenAI
from pydantic import BaseModel

client = AsyncOpenAI(
    base_url="https://getheli.ru/v1",
    api_key=os.environ["HELI_API_KEY"],
    timeout=60.0,
    max_retries=2,
)
app = FastAPI()

class ChatRequest(BaseModel):
    message: str

@app.post("/api/chat")
async def chat(body: ChatRequest):
    stream = await client.chat.completions.create(
        model="openai/gpt-5.4-mini",
        messages=[{"role": "user", "content": body.message}],
        max_tokens=1000,
        stream=True,
    )

    async def tokens():
        async for chunk in stream:
            if chunk.choices and chunk.choices[0].delta.content:
                yield chunk.choices[0].delta.content

    return StreamingResponse(tokens(), media_type="text/plain; charset=utf-8")

Streaming в Heli API

Что не отдавать клиенту

  • Не принимайте с фронтенда параметр model и полный список messages без проверки: иначе любой пользователь сможет выбрать самую дорогую модель или подменить системный промпт.
  • Системный промпт, модель и max_tokens задавайте на сервере.
  • Добавьте свою авторизацию и rate limit на /api/chat по пользователю: публичный эндпоинт без ограничений быстро израсходует баланс.
  • Не возвращайте клиенту текст ошибок Heli как есть — логируйте их на сервере.

Безопасность API-ключа

Ошибки и таймауты

  • openai.RateLimitError (429) — SDK повторит запрос сам с учётом Retry-After; если ошибка дошла до вас, верните клиенту 503 с просьбой повторить позже.
  • openai.APIStatusError со статусом 402 — закончился баланс; повторы не помогут, нужен алерт администратору.
  • Таймаут клиента задавайте явно: для коротких ответов хватает десятков секунд, длинные ответы лучше стримить.
  • Если перед FastAPI стоит nginx, отключите буферизацию для стриминга (proxy_buffering off), иначе токены придут одним куском.

Ошибка 429

Ошибка 402

Таймауты chat completions

Какую модель взять

Для чат-функций в продукте обычно хватает быстрой модели, а сильную стоит подключать только к сложным сценариям. Модель меняется одной строкой, так что её легко сравнить на своих данных.

GPT-5.4 mini

Gemini 3.5 Flash

Claude Sonnet 5

Python SDK + Heli

Ответ без стриминга

Если интерфейсу не нужен посимвольный вывод, верните JSON с полем text из resp.choices[0].message.content и, при необходимости, resp.usage для своей статистики. Авторизацию пользователя удобно вынести в зависимость FastAPI (Depends), а сервер запускать через uvicorn или gunicorn с несколькими воркерами. Долгие задачи, например обработку документов, лучше отдавать в очередь фоновых задач и возвращать клиенту идентификатор задачи, а не держать HTTP-запрос открытым минутами.

Попробуйте Heli

Ключ sk-heli- появляется после подтверждения почты. Через Google и Яндекс — сразу в кабинете.

Документация API · Модели и цены

Читайте также