- @waylaidwanderer/fetch-event-source를 활용한 Node Express server 예제입니다.
modules/FetchEventSource.js
const { fetch, Headers, Request, Response } = require('fetch-undici'); if (!globalThis.fetch) { globalThis.fetch = fetch; globalThis.Headers = Headers; globalThis.Request = Request; globalThis.Response = Response; } module.exports = require('@waylaidwanderer/fetch-event-source');
server.js
const express = require('express'); const bodyParser = require('body-parser'); const { fetchEventSource } = require('./modules/FetchEventSource'); const GPT_API_URL = 'https://api.openai.com/v1/chat/completions'; // API URL const GPT_API_KEY = ''; // API KEY const app = express(); const router = express.Router(); router.post('/request', (request, response) => { const bodyData = Object.assign({}, request.body, { model: 'gpt-3.5-turbo', stream: true }); requestGPT(response, bodyData) .then(() => response.end()) .catch((error) => response.status(error.status).json(error).end()); }); /** * GPT 서버로 요청합니다. * @param {Response} response * @param {Object} bodyData * @returns */ function requestGPT(response, bodyData) { const abortController = new AbortController(); return new Promise(async (resolve, reject) => { try { await fetchEventSource(GPT_API_URL, { method: 'post', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${GPT_API_KEY}` }, body: JSON.stringify(bodyData), signal: abortController.signal, onopen: async (openResponse) => { if (openResponse.status === 200) { response.on('close', () => { abortController.abort(); resolve(); }); response.set({ 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Content-Type': 'text/event-stream' }); response.flushHeaders(); return; } let error; try { const json = await openResponse.json(); error = json; } catch (e) { error = e; } error.status = openResponse.status; throw error; }, onclose: () => resolve(), onerror: (error) => { reject(error); throw error; }, onmessage: (message) => { if (!message.data || message.event === 'ping') { return; } response.write('data:' + message.data + '\n\n'); }, }); } catch (e) { reject(e); } }); } app.use(bodyParser.json()); app.use('/', router); app.listen(8080);