Правильная схема для веб- и мобильных приложений: фронтенд → ваш FastAPI → Heli API. Ключ sk-heli- хранится только в переменных окружения сервера и никогда не попадает в браузер. FastAPI асинхронный, поэтому используйте AsyncOpenAI: синхронный клиент внутри async-обработчика блокирует event loop, и сервер перестаёт отвечать другим запросам, пока модель генерирует ответ.
Маршрут со стримингом
Проверка chunk.choices обязательна: Heli включает в поток итоговый чанк с данными usage, а у него список choices пустой. Без проверки код упадёт с IndexError на последнем чанке.
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")Что не отдавать клиенту
- Не принимайте с фронтенда параметр model и полный список messages без проверки: иначе любой пользователь сможет выбрать самую дорогую модель или подменить системный промпт.
- Системный промпт, модель и max_tokens задавайте на сервере.
- Добавьте свою авторизацию и rate limit на /api/chat по пользователю: публичный эндпоинт без ограничений быстро израсходует баланс.
- Не возвращайте клиенту текст ошибок Heli как есть — логируйте их на сервере.
Ошибки и таймауты
- openai.RateLimitError (429) — SDK повторит запрос сам с учётом Retry-After; если ошибка дошла до вас, верните клиенту 503 с просьбой повторить позже.
- openai.APIStatusError со статусом 402 — закончился баланс; повторы не помогут, нужен алерт администратору.
- Таймаут клиента задавайте явно: для коротких ответов хватает десятков секунд, длинные ответы лучше стримить.
- Если перед FastAPI стоит nginx, отключите буферизацию для стриминга (proxy_buffering off), иначе токены придут одним куском.
Какую модель взять
Для чат-функций в продукте обычно хватает быстрой модели, а сильную стоит подключать только к сложным сценариям. Модель меняется одной строкой, так что её легко сравнить на своих данных.
Ответ без стриминга
Если интерфейсу не нужен посимвольный вывод, верните JSON с полем text из resp.choices[0].message.content и, при необходимости, resp.usage для своей статистики. Авторизацию пользователя удобно вынести в зависимость FastAPI (Depends), а сервер запускать через uvicorn или gunicorn с несколькими воркерами. Долгие задачи, например обработку документов, лучше отдавать в очередь фоновых задач и возвращать клиенту идентификатор задачи, а не держать HTTP-запрос открытым минутами.
Попробуйте Heli
Ключ sk-heli- появляется после подтверждения почты. Через Google и Яндекс — сразу в кабинете.