Начать пользоваться на www.aijora.ru
AijoraДокументация
Функции

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

Потоковая передача ответов в реальном времени

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

Как работает потоковая передача

Без стриминга (обычный режим)

[Пользователь отправляет запрос]

[Модель думает и генерирует весь текст]

    ⏳ Ожидание...

[Возвращается полный ответ в JSON]

Время отклика = время генерации текста

Пользователь ждёт 5-30 секунд до первого символа ответа.

Со стримингом

[Пользователь отправляет запрос]

[Модель начинает генерацию]

    ⚡ Сразу → "При" 
    ⚡ Сразу → "вет"
    ⚡ Сразу → "! Как"
    ⚡ Сразу → " дела"
    ⚡ Сразу → "?"

    [DONE]

Первый символ приходит за ~0.5-2 секунды

Пользователь видит "живой" ответ, как в ChatGPT.

Включение потоковой передачи

Добавьте параметр stream: true в запрос:

{
  "model": "gpt-4o-mini",
  "messages": [
    { "role": "user", "content": "Расскажи историю" }
  ],
  "stream": true
}

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

Ответ приходит в формате Server-Sent Events (SSE). Каждая порция данных начинается с data: и содержит JSON с частичным ответом.

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

data: {"id":"chatcmpl-123","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"id":"chatcmpl-123","choices":[{"index":0,"delta":{"content":"При"},"finish_reason":null}]}

data: {"id":"chatcmpl-123","choices":[{"index":0,"delta":{"content":"вет"},"finish_reason":null}]}

data: {"id":"chatcmpl-123","choices":[{"index":0,"delta":{"content":"! Как"},"finish_reason":null}]}

data: {"id":"chatcmpl-123","choices":[{"index":0,"delta":{"content":" дела"},"finish_reason":null}]}

data: {"id":"chatcmpl-123","choices":[{"index":0,"delta":{"content":"?"},"finish_reason":null}]}

data: {"id":"chatcmpl-123","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":10,"completion_tokens":5,"total_tokens":15}}

data: [DONE]

Ключевые моменты:

  • Каждая строка начинается с data:
  • Между событиями — пустые строки
  • В delta.content — очередной кусочек текста
  • finish_reason: "stop" — генерация завершена
  • [DONE] — поток закрыт

Примеры реализации

cURL (просмотр потока)

curl -N https://api.aijora.com/api/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AIJORA_API_KEY" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [
      {
        "role": "user",
        "content": "Расскажи короткую историю про кота"
      }
    ],
    "stream": true
  }'

Флаг -N отключает буферизацию для потоковых данных.

JavaScript (Fetch API)

async function streamChat(message) {
  const response = await fetch('https://api.aijora.com/api/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.AIJORA_API_KEY}`
    },
    body: JSON.stringify({
      model: 'gpt-4o-mini',
      messages: [{ role: 'user', content: message }],
      stream: true
    })
  });

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

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    // Декодируем chunk
    const chunk = decoder.decode(value);
    
    // Разбиваем на строки
    const lines = chunk.split('\n').filter(line => line.trim() !== '');
    
    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = line.slice(6); // Убираем "data: "
        
        if (data === '[DONE]') {
          console.log('\n[Стрим завершён]');
          return;
        }
        
        try {
          const parsed = JSON.parse(data);
          const content = parsed.choices[0]?.delta?.content;
          
          if (content) {
            process.stdout.write(content); // Выводим без переноса строки
          }
        } catch (e) {
          // Игнорируем ошибки парсинга
        }
      }
    }
  }
}

// Использование
streamChat('Расскажи про JavaScript');
async function streamChatWithAccumulation(message, onChunk, onComplete) {
  let fullText = '';
  
  const response = await fetch('https://api.aijora.com/api/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.AIJORA_API_KEY}`
    },
    body: JSON.stringify({
      model: 'gpt-4o-mini',
      messages: [{ role: 'user', content: message }],
      stream: true
    })
  });

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

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split('\n');
    
    // Последняя строка может быть неполной
    buffer = lines.pop() || '';
    
    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = line.slice(6);
        
        if (data === '[DONE]') {
          onComplete(fullText);
          return;
        }
        
        try {
          const parsed = JSON.parse(data);
          const content = parsed.choices[0]?.delta?.content;
          
          if (content) {
            fullText += content;
            onChunk(content, fullText); // Передаём chunk и полный текст
          }
        } catch (e) {
          // Игнорируем
        }
      }
    }
  }
}

// Использование
streamChatWithAccumulation(
  'Расскажи анекдот',
  (chunk, fullText) => {
    console.log('Новый chunk:', chunk);
    // Обновляем UI с fullText
  },
  (fullText) => {
    console.log('Готово! Полный текст:', fullText);
  }
);
import { useState } from 'react';

function useStreamChat() {
  const [content, setContent] = useState('');
  const [isStreaming, setIsStreaming] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const sendMessage = async (message: string) => {
    setContent('');
    setIsStreaming(true);
    setError(null);

    try {
      const response = await fetch('https://api.aijora.com/api/v1/chat/completions', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${process.env.NEXT_PUBLIC_AIJORA_API_KEY}`
        },
        body: JSON.stringify({
          model: 'gpt-4o-mini',
          messages: [{ role: 'user', content: message }],
          stream: true
        })
      });

      const reader = response.body?.getReader();
      if (!reader) throw new Error('No reader');

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

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

        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';

        for (const line of lines) {
          if (line.startsWith('data: ')) {
            const data = line.slice(6);
            if (data === '[DONE]') continue;

            try {
              const parsed = JSON.parse(data);
              const delta = parsed.choices[0]?.delta?.content;
              if (delta) {
                setContent(prev => prev + delta);
              }
            } catch (e) {
              // Игнорируем
            }
          }
        }
      }
    } catch (err) {
      setError(err as Error);
    } finally {
      setIsStreaming(false);
    }
  };

  return { content, isStreaming, error, sendMessage };
}

// Компонент
function ChatComponent() {
  const { content, isStreaming, sendMessage } = useStreamChat();

  return (
    <div>
      <div className="response">
        {content}
        {isStreaming && <span className="cursor"></span>}
      </div>
      <button onClick={() => sendMessage('Привет!')}>
        Отправить
      </button>
    </div>
  );
}

Python (Requests)

import os
import json
import requests

def stream_chat(message):
    response = requests.post(
        'https://api.aijora.com/api/v1/chat/completions',
        headers={
            'Authorization': f'Bearer {os.getenv("AIJORA_API_KEY")}',
            'Content-Type': 'application/json'
        },
        json={
            'model': 'gpt-4o-mini',
            'messages': [{'role': 'user', 'content': message}],
            'stream': True
        },
        stream=True  # Важно!
    )

    for line in response.iter_lines():
        if line:
            line = line.decode('utf-8')
            
            if line.startswith('data: '):
                data = line[6:]  # Убираем "data: "
                
                if data == '[DONE]':
                    print('\n[Стрим завершён]')
                    break
                
                try:
                    parsed = json.loads(data)
                    content = parsed['choices'][0]['delta'].get('content', '')
                    
                    if content:
                        print(content, end='', flush=True)
                except json.JSONDecodeError:
                    pass

# Использование
stream_chat('Расскажи про Python')
import os
import json
import requests
from typing import Callable

def stream_chat(
    message: str,
    on_chunk: Callable[[str], None],
    on_complete: Callable[[str], None]
):
    full_text = ''
    
    response = requests.post(
        'https://api.aijora.com/api/v1/chat/completions',
        headers={
            'Authorization': f'Bearer {os.getenv("AIJORA_API_KEY")}'
        },
        json={
            'model': 'gpt-4o-mini',
            'messages': [{'role': 'user', 'content': message}],
            'stream': True
        },
        stream=True
    )

    for line in response.iter_lines():
        if line:
            line = line.decode('utf-8')
            
            if line.startswith('data: '):
                data = line[6:]
                
                if data == '[DONE]':
                    on_complete(full_text)
                    break
                
                try:
                    parsed = json.loads(data)
                    content = parsed['choices'][0]['delta'].get('content', '')
                    
                    if content:
                        full_text += content
                        on_chunk(content)
                except json.JSONDecodeError:
                    pass

# Использование
stream_chat(
    'Расскажи историю',
    on_chunk=lambda chunk: print(chunk, end='', flush=True),
    on_complete=lambda text: print(f'\n\nВсего символов: {len(text)}')
)
import os
import json
import aiohttp
import asyncio

async def stream_chat_async(message: str):
    async with aiohttp.ClientSession() as session:
        async with session.post(
            'https://api.aijora.com/api/v1/chat/completions',
            headers={
                'Authorization': f'Bearer {os.getenv("AIJORA_API_KEY")}',
                'Content-Type': 'application/json'
            },
            json={
                'model': 'gpt-4o-mini',
                'messages': [{'role': 'user', 'content': message}],
                'stream': True
            }
        ) as response:
            async for line in response.content:
                line = line.decode('utf-8').strip()
                
                if line.startswith('data: '):
                    data = line[6:]
                    
                    if data == '[DONE]':
                        print('\n[Завершено]')
                        break
                    
                    try:
                        parsed = json.loads(data)
                        content = parsed['choices'][0]['delta'].get('content', '')
                        
                        if content:
                            print(content, end='', flush=True)
                            await asyncio.sleep(0)  # Даём другим задачам выполниться
                    except json.JSONDecodeError:
                        pass

# Использование
asyncio.run(stream_chat_async('Привет!'))

OpenAI SDK

import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: process.env.AIJORA_API_KEY,
  baseURL: 'https://api.aijora.com/api/v1'
});

async function streamWithSDK() {
  const stream = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [{ role: 'user', content: 'Расскажи историю' }],
    stream: true
  });

  for await (const chunk of stream) {
    const content = chunk.choices[0]?.delta?.content || '';
    process.stdout.write(content);
  }
}

streamWithSDK();
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv('AIJORA_API_KEY'),
    base_url='https://api.aijora.com/api/v1'
)

stream = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=[{'role': 'user', 'content': 'Расскажи историю'}],
    stream=True
)

for chunk in stream:
    content = chunk.choices[0].delta.content or ''
    print(content, end='', flush=True)

Особенности

  • Стоимость не зависит от использования streaming — тарифицируются только токены
  • Информация о токенах (usage) приходит в последнем chunk перед [DONE]
  • Нельзя использовать n > 1 (несколько вариантов) в режиме потоковой передачи