Документация

Потоковая передача

Потоковая передача позволяет получать ответы модели в реальном времени по мере их генерации, вместо ожидания полного завершения. Это улучшает пользовательский опыт, особенно для длинных ответов.

Как включить потоковую передачу

Чтобы включить потоковую передачу, установите параметр stream в значение true в вашем запросе:

fetch('https://llmost.ru/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer <LLMOST_API_KEY>',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'openai/gpt-4o',
    messages: [
      { role: 'user', content: 'Расскажи мне историю' },
    ],
    stream: true, // Включить потоковую передачу
  }),
});

Формат Server-Sent Events (SSE)

Потоковые ответы используют формат Server-Sent Events (SSE). Каждое событие содержит данные в формате JSON с информацией о генерируемом тексте.

Структура события

data: {"id":"gen-xxx","choices":[{"delta":{"content":"Привет"},"index":0}],"model":"openai/gpt-4o","object":"chat.completion.chunk"}

data: {"id":"gen-xxx","choices":[{"delta":{"content":" мир"},"index":0}],"model":"openai/gpt-4o","object":"chat.completion.chunk"}

data: [DONE]

Комментарии в потоке

Поток SSE может иногда содержать строки-комментарии (начинающиеся с :), которые используются для поддержания соединения активным. Эти строки следует игнорировать при обработке.

Примеры кода

Python

import requests
import json

url = "https://llmost.ru/api/v1/chat/completions"
headers = {
  "Authorization": f"Bearer {API_KEY}",
  "Content-Type": "application/json"
}

payload = {
  "model": "openai/gpt-4o",
  "messages": [{"role": "user", "content": "Напиши короткую историю"}],
  "stream": True
}

buffer = ""
with requests.post(url, headers=headers, json=payload, stream=True) as r:
  for chunk in r.iter_content(chunk_size=1024, decode_unicode=True):
    buffer += chunk
    while True:
      # Найти следующую полную строку SSE
      line_end = buffer.find('\n')
      if line_end == -1:
        break

      line = buffer[:line_end].strip()
      buffer = buffer[line_end + 1:]

      if line.startswith('data: '):
        data = line[6:]
        if data == '[DONE]':
          break

        try:
          data_obj = json.loads(data)
          content = data_obj["choices"][0]["delta"].get("content")
          if content:
            print(content, end="", flush=True)
        except json.JSONDecodeError:
          pass

TypeScript

const response = await fetch('https://llmost.ru/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'openai/gpt-4o',
    messages: [{ role: 'user', content: 'Напиши короткую историю' }],
    stream: true,
  }),
});

const reader = response.body?.getReader();
if (!reader) {
  throw new Error('Response body is not readable');
}

const decoder = new TextDecoder();
let buffer = '';

try {
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    // Добавить новый фрагмент в буфер
    buffer += decoder.decode(value, { stream: true });

    // Обработать полные строки из буфера
    while (true) {
      const lineEnd = buffer.indexOf('\n');
      if (lineEnd === -1) break;

      const line = buffer.slice(0, lineEnd).trim();
      buffer = buffer.slice(lineEnd + 1);

      if (line.startsWith('data: ')) {
        const data = line.slice(6);
        if (data === '[DONE]') break;

        try {
          const parsed = JSON.parse(data);
          const content = parsed.choices[0].delta.content;
          if (content) {
            console.log(content);
          }
        } catch (e) {
          // Игнорировать невалидный JSON
        }
      }
    }
  }
} finally {
  reader.cancel();
}

Обработка ошибок

Ошибки до начала потока

Если ошибка происходит до начала генерации токенов, вы получите стандартный JSON-ответ с ошибкой и соответствующим HTTP-статусом:

{
  "error": {
    "code": 400,
    "message": "Invalid request: model not found",
    "metadata": {}
  }
}

Ошибки во время потока

Если ошибка происходит во время генерации токенов, она будет отправлена как Server-Sent Event с полем error в объекте choices:

{
  "id": "gen-xxx",
  "choices": [
    {
      "delta": { "content": null },
      "error": {
        "code": 500,
        "message": "Provider error: rate limit exceeded",
        "metadata": {}
      },
      "finish_reason": "error"
    }
  ],
  "model": "openai/gpt-4o",
  "object": "chat.completion.chunk"
}

После отправки события с ошибкой поток будет завершён.

Отмена потока

Многие провайдеры поддерживают отмену потоковых запросов. Это позволяет остановить генерацию, если пользователь больше не нуждается в ответе.

Python (с использованием Threading)

import requests
import threading

def stream_request(event: threading.Event):
    with requests.post(url, headers=headers, json=payload, stream=True) as r:
        for chunk in r.iter_content(chunk_size=1024):
            if event.is_set():
                break
            # Обработать chunk

stop_event = threading.Event()
thread = threading.Thread(target=stream_request, args=(stop_event,))
thread.start()

# Отменить поток когда необходимо
stop_event.set()
thread.join()

TypeScript (с использованием AbortController)

const controller = new AbortController();

fetch('https://llmost.ru/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'openai/gpt-4o',
    messages: [{ role: 'user', content: 'Напиши длинную историю' }],
    stream: true,
  }),
  signal: controller.signal, // Передать сигнал для отмены
})
.then(async (response) => {
  // Обработать поток
})
.catch((error) => {
  if (error.name === 'AbortError') {
    console.log('Запрос был отменён');
  }
});

// Отменить запрос когда необходимо
controller.abort();

Использование данных токенов

При потоковой передаче информация об использовании токенов (usage) отправляется в конце потока в виде отдельного события:

{
  "id": "gen-xxx",
  "choices": [],
  "usage": {
    "prompt_tokens": 20,
    "completion_tokens": 150,
    "total_tokens": 170
  },
  "model": "openai/gpt-4o",
  "object": "chat.completion.chunk"
}

Точный подсчёт токенов

Для получения точной информации об использовании токенов и стоимости используйте эндпоинт /api/v1/generation с id из ответа. См. Generation для деталей.

Рекомендуемые библиотеки

Для упрощения работы с SSE рекомендуется использовать специализированные библиотеки:

  • JavaScript/TypeScript: eventsource-parser
  • Python: Встроенная поддержка через requests с stream=True
  • Различные языки: OpenAI SDK (совместим с LLMost)
  • React/Next.js: Vercel AI SDK

Лучшие практики

  1. Обработка буферизации: Всегда буферизуйте входящие данные и обрабатывайте полные строки
  2. Обработка ошибок: Проверяйте наличие ошибок как до, так и во время потока
  3. Управление соединением: Используйте таймауты и обрабатывайте разрывы соединения
  4. Отмена запросов: Реализуйте механизм отмены для улучшения пользовательского опыта
  5. Игнорирование комментариев: Пропускайте строки, начинающиеся с : (keep-alive сообщения)
Потоковая передача | Документация | LLMost