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

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

Как читать SSE-ответы и потоковые обновления

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

Чтобы включить потоковую передачу, установите параметр stream в true в вашем запросе. Модель будет передавать ответ клиенту по частям (чанками), а не возвращать весь ответ сразу.

Пример обработки потока

Python

import requests
import json

response = requests.post(
  url="https://neuroapi.host/v1/chat/completions",
  headers={
    "Authorization": "Bearer <YOUR_API_KEY>",
    "Content-Type": "application/json"
  },
  json={
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "Напиши длинную историю о космосе"}],
    "stream": True
  },
  stream=True
)

buffer = ""
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
  buffer += chunk
  while '\n\n' in buffer:
    line, buffer = buffer.split('\n\n', 1)
    if line.startswith('data: '):
      data = line[6:]
      if data == '[DONE]':
        print("\n\n--- Стрим завершён ---")
        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:
        # Игнорируем некорректный JSON, это может быть ping-сообщение
        pass

TypeScript

interface ChatMessage {
  role: string;
  content: string;
}

interface ChatChoice {
  delta: {
    content?: string;
  };
}

interface ChatCompletionResponse {
  choices: ChatChoice[];
}

async function streamExample(): Promise<void> {
  try {
    const response = await fetch("https://neuroapi.host/v1/chat/completions", {
      method: "POST",
      headers: {
        Authorization: "Bearer <YOUR_API_KEY>",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        model: "gpt-4o",
        messages: [{ role: "user", content: "Напиши длинную историю о космосе" }],
        stream: true,
      }),
    });

    if (!response.ok) {
      throw new Error('HTTP error! status: ' + response.status);
    }

    if (!response.body) {
      throw new Error("Response body is null");
    }

    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = "";

    console.log("Начинаем получение ответа...\n");

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

      buffer += decoder.decode(value, { stream: true });

      let boundary = buffer.indexOf("\n\n");
      while (boundary !== -1) {
        const chunk = buffer.substring(0, boundary);
        buffer = buffer.substring(boundary + 2);

        if (chunk.startsWith("data: ")) {
          const data = chunk.substring(6);
          if (data === "[DONE]") {
            console.log("\n\n--- Стрим завершён ---");
            return;
          }
          try {
            const parsed: ChatCompletionResponse = JSON.parse(data);
            const content = parsed.choices?.[0]?.delta?.content;
            if (content) {
              process.stdout.write(content);
            }
          } catch (e) {
            // Игнорируем ошибки парсинга, это могут быть ping-сообщения
          }
        }
        boundary = buffer.indexOf("\n\n");
      }
    }
  } catch (error) {
    console.error("Ошибка при выполнении запроса:", error);
    throw error;
  }
}

Почему ответ может начинаться долго

У моделей семейств GPT-5.6 и Claude рассуждение (reasoning) скрыто на стороне провайдера: процесс «размышления» модели не передаётся в поток. Стриминг начинается только тогда, когда рассуждение закончилось и модель начала генерировать сам ответ. Поэтому время до первого токена может быть долгим — у тяжёлых моделей (например, Claude Opus или GPT-5.6 Sol) оно честно достигает нескольких минут, особенно на сложных задачах.

Это не зависание: соединение остаётся живым, а keep-alive сообщения : PING продолжают приходить, пока модель «думает». Мы автоматически ждём такие ответы с увеличенным бюджетом и сами переключаемся на резервный маршрут, только если провайдер действительно завис.

Рекомендация: не ставьте в своём клиенте короткий таймаут на первый токен для моделей с рассуждением. Ориентируйтесь на 120 секунд для лёгких моделей и до 600 секунд для тяжёлых (Claude Opus, GPT-5.6 Sol и других с глубоким рассуждением) — иначе вы разорвёте соединение раньше, чем модель успеет ответить.

Дополнительная информация

Keep-Alive сообщения: Для поддержания соединения API периодически отправляет keep-alive сообщения в формате : PING. Эти сообщения не начинаются с data: и должны игнорироваться вашим парсером событий (SSE). Наши примеры кода уже учитывают это.

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

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

Для этого в JavaScript используется AbortController, а в Python можно использовать более сложные механизмы, такие как threading.Event для сигнализации об отмене.

TypeScript

const controller = new AbortController();

async function cancelableStream() {
  try {
    const response = await fetch(
      'https://neuroapi.host/v1/chat/completions',
      {
        method: 'POST',
        headers: {
          Authorization: 'Bearer <YOUR_API_KEY>',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          model: 'gpt-4o',
          messages: [{ role: 'user', content: 'Напиши очень длинную историю' }],
          stream: true,
        }),
        signal: controller.signal, // Передаём AbortSignal
      },
    );

    // ... обработка стрима
    
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Поток был отменён');
    } else {
      console.error('Произошла ошибка:', error);
    }
  }
}

// Запускаем стрим
cancelableStream();

// Отменяем стрим через 1 секунду
setTimeout(() => {
  controller.abort();
}, 1000);

Python

import requests
import threading
import time

# Event для сигнализации об отмене
cancel_event = threading.Event()

def stream_with_cancellation():
    try:
        with requests.post(
            "https://neuroapi.host/v1/chat/completions",
            headers={"Authorization": "Bearer <YOUR_API_KEY>"},
            json={"model": "gpt-4o", "messages": [{"role": "user", "content": "Напиши очень длинную историю"}], "stream": True},
            stream=True,
            timeout=10 # Таймаут на установку соединения
        ) as response:
            response.raise_for_status()
            for chunk in response.iter_content(chunk_size=8192):
                if cancel_event.is_set():
                    print("\n--- Поток отменён ---")
                    break
                print(chunk.decode('utf-8'), end="")

    except requests.exceptions.RequestException as e:
        if cancel_event.is_set():
            print("\n--- Запрос отменён успешно ---")
        else:
            print(f"Произошла ошибка: {e}")

# Запускаем стрим в отдельном потоке
stream_thread = threading.Thread(target=stream_with_cancellation)
stream_thread.start()

# Ждём 1 секунду и отменяем
time.sleep(1)
print("\n--- Отправка сигнала отмены ---")
cancel_event.set()

stream_thread.join()

Важно: Отмена работает только для потоковых запросов к поддерживающим отмену провайдерам. Для не-потоковых запросов или неподдерживаемых провайдеров модель продолжит обработку, и вам будет выставлен счет за полный ответ.

On this page