Skip to content

Instantly share code, notes, and snippets.

@composite
Created May 9, 2024 13:19
Show Gist options
  • Save composite/bbd22d1bdf4efbc52ddda7905651e753 to your computer and use it in GitHub Desktop.
Save composite/bbd22d1bdf4efbc52ddda7905651e753 to your computer and use it in GitHub Desktop.
Next.js Server Sent Event Example (Next.js 13+ App Router)
// app/api/route.ts
import { Configuration, OpenAIApi } from 'openai';
export const runtime = 'nodejs';
// This is required to enable streaming
export const dynamic = 'force-dynamic';
export async function GET() {
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
let responseStream = new TransformStream();
const writer = responseStream.writable.getWriter();
const encoder = new TextEncoder();
writer.write(encoder.encode('Vercel is a platform for....'));
try {
const openaiRes = await openai.createCompletion(
{
model: 'text-davinci-002',
prompt: 'Vercel is a platform for',
max_tokens: 100,
temperature: 0,
stream: true,
},
{ responseType: 'stream' }
);
// @ts-ignore
openaiRes.data.on('data', async (data: Buffer) => {
const lines = data
.toString()
.split('\n')
.filter((line: string) => line.trim() !== '');
for (const line of lines) {
const message = line.replace(/^data: /, '');
if (message === '[DONE]') {
console.log('Stream completed');
writer.close();
return;
}
try {
const parsed = JSON.parse(message);
await writer.write(encoder.encode(`${parsed.choices[0].text}`));
} catch (error) {
console.error('Could not JSON parse stream message', message, error);
}
}
});
} catch (error) {
console.error('An error occurred during OpenAI request', error);
writer.write(encoder.encode('An error occurred during OpenAI request'));
writer.close();
}
return new Response(responseStream.readable, {
headers: {
'Content-Type': 'text/event-stream',
Connection: 'keep-alive',
'Cache-Control': 'no-cache, no-transform',
},
});
}
// app/api/netusage/route.ts
import EventSource from "eventsource";
// should be declared (!)
export const dynamic = 'force-dynamic';
export async function GET() {
let responseStream = new TransformStream();
const writer = responseStream.writable.getWriter();
const encoder = new TextEncoder();
const resp = new EventSource("http://localhost:3333/api/netusage")
resp.onmessage = async (e) => {
await writer.write(encoder.encode(`event: message\ndata: ${e.data}\n\n`));
}
resp.onerror = () => {
resp.close();
await writer.close();
}
return new Response(responseStream.readable, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
},
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment