serp-chat / src /pages /api /stream.js
matt HOFFNER
improvements
53763e2
import { createParser } from 'eventsource-parser';
export const OPENAI_API_HOST = process.env.OPENAI_API_HOST || "https://api.openai.com";
export const OPENAI_API_TYPE = process.env.OPENAI_API_TYPE || "openai";
export class LLMError extends Error {
constructor(message, type, param, code) {
super(message);
this.name = 'LLMError';
this.type = type;
this.param = param;
this.code = code;
}
}
export const LLMStream = async (
model,
systemPrompt,
temperature,
messages,
functions
) => {
let url = `${OPENAI_API_HOST}/v1/chat/completions`;
const res = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...(OPENAI_API_TYPE === 'openai' && {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`
})
},
method: 'POST',
body: JSON.stringify({
...(OPENAI_API_TYPE === 'openai' && {model: model.id}),
messages: [
{
role: 'system',
content: systemPrompt,
},
...messages,
],
max_tokens: 1000,
temperature: temperature,
functions: [functions['googleCustomSearch']['googleCustomSearchSchema']],
stream: true,
}),
});
const encoder = new TextEncoder();
const decoder = new TextDecoder();
if (res.status !== 200) {
const result = await res.json();
if (result.error) {
throw new LLMError(
result.error.message,
result.error.type,
result.error.param,
result.error.code,
);
} else {
throw new Error(
`OpenAI API returned an error: ${
decoder.decode(result?.value) || result.statusText
}`,
);
}
}
const stream = new ReadableStream({
async start(controller) {
let func_call = {
"name": null,
"arguments": "",
};
const onParse = async (event) => {
if (event.type === 'event') {
const data = event.data;
try {
if (data === "[DONE]" || !data) {
return;
}
const json = JSON.parse(data);
if (Array.isArray(json.choices) && json.choices.length > 0) {
const choice = json.choices[0];
const delta = choice.delta;
if (choice.finish_reason === "stop") {
controller.close();
return;
}
if (delta.hasOwnProperty("function_call")) {
if (delta.function_call.hasOwnProperty("name")) {
func_call["name"] = delta.function_call["name"];
}
if (delta.function_call.hasOwnProperty("arguments")) {
func_call["arguments"] += delta.function_call["arguments"];
}
}
if (choice.finish_reason === "function_call") {
// function call here using func_call
const fn = functions[func_call.name][func_call.name];
const funcResult = await fn(func_call.arguments);
const serpQueue = encoder.encode(funcResult);
controller.enqueue(serpQueue);
}
if (delta && 'content' in delta) {
const text = delta.content;
const queue = encoder.encode(text);
controller.enqueue(queue);
}
} else {
console.error('No choices found in json');
}
} catch (e) {
console.log(e);
controller.error(e);
}
}
};
const parser = createParser(onParse);
for await (const chunk of res.body) {
parser.feed(decoder.decode(chunk));
}
},
});
return stream;
};