Потоковая передача
Потоковая передача позволяет получать ответы модели в реальном времени по мере их генерации, вместо ожидания полного завершения. Это улучшает пользовательский опыт, особенно для длинных ответов.
Как включить потоковую передачу
Чтобы включить потоковую передачу, установите параметр 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:
passTypeScript
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
Лучшие практики
- Обработка буферизации: Всегда буферизуйте входящие данные и обрабатывайте полные строки
- Обработка ошибок: Проверяйте наличие ошибок как до, так и во время потока
- Управление соединением: Используйте таймауты и обрабатывайте разрывы соединения
- Отмена запросов: Реализуйте механизм отмены для улучшения пользовательского опыта
- Игнорирование комментариев: Пропускайте строки, начинающиеся с
:(keep-alive сообщения)