Реалізація Streaming AI-відповідей (Server-Sent Events) на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація Streaming AI-відповідей (Server-Sent Events) на сайті
Середня
~2-3 робочих дні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Потокова передача AI відповідей через Server-Sent Events (SSE)

Потокова передача відповідей LLM токен-за-токеном до браузерів для реал-тайм, чутливого UX. Не потрібно чекати завершення генерації повної відповіді.

Реалізація сервера

import OpenAI from 'openai';

const openai = new OpenAI();

export async function POST(request) {
  const { message } = await request.json();

  const stream = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [{ role: 'user', content: message }],
    stream: true,
  });

  return new Response(
    (async function* () {
      for await (const chunk of stream) {
        const content = chunk.choices[0]?.delta?.content || '';
        if (content) {
          yield `data: ${JSON.stringify({ content })}\n\n`;
        }
      }
      yield 'data: [DONE]\n\n';
    })(),
    {
      headers: {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
      },
    }
  );
}

Реалізація клієнта

async function streamResponse(message, onChunk) {
  const response = await fetch('/api/stream', {
    method: 'POST',
    body: JSON.stringify({ message }),
  });

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

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

    const text = decoder.decode(value);
    const lines = text.split('\n');

    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = JSON.parse(line.slice(6));
        if (data.content) {
          fullResponse += data.content;
          onChunk(data.content);
        }
      }
    }
  }

  return fullResponse;
}

React компонент

function StreamingChat() {
  const [response, setResponse] = useState('');
  const [streaming, setStreaming] = useState(false);

  async function handleSubmit(message) {
    setResponse('');
    setStreaming(true);

    await streamResponse(message, (chunk) => {
      setResponse(prev => prev + chunk);
    });

    setStreaming(false);
  }

  return (
    <div>
      <textarea
        onChange={(e) => handleSubmit(e.target.value)}
        placeholder="Запитай щось..."
      />
      <div className="response">
        {response}
        {streaming && <span className="cursor">|</span>}
      </div>
    </div>
  );
}

Обробка помилок

async function robustStream(message, onChunk, onError) {
  try {
    await streamResponse(message, onChunk);
  } catch (error) {
    if (error.name === 'AbortError') {
      onError('З\'єднання переривається');
    } else {
      onError(`Помилка: ${error.message}`);
    }
  }
}

Терміни

  • Базовий SSE стримінг — 1–2 дні
  • Обробка помилок + переконнект — 1 день
  • UI поліш (курсор, анімація) — 1–2 дні
  • Оптимізація продуктивності — 1 день