Add openaistream

This commit is contained in:
Logan Yang 2023-04-05 09:14:01 -07:00
parent b9e8b7dc78
commit 27eea8d7d2
3 changed files with 113 additions and 0 deletions

9
package-lock.json generated
View file

@ -11,6 +11,7 @@
"dependencies": {
"axios": "^1.3.4",
"esbuild-plugin-svg": "^0.1.0",
"eventsource-parser": "^1.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
@ -1465,6 +1466,14 @@
"node": ">=0.10.0"
}
},
"node_modules/eventsource-parser": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.0.0.tgz",
"integrity": "sha512-9jgfSCa3dmEme2ES3mPByGXfgZ87VbP97tng1G2nWwWx6bV2nYxm2AWCrbQjXToSe+yYlqaZNtxffR9IeQr95g==",
"engines": {
"node": ">=14.18"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",

View file

@ -26,6 +26,7 @@
"dependencies": {
"axios": "^1.3.4",
"esbuild-plugin-svg": "^0.1.0",
"eventsource-parser": "^1.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}

103
src/openAiStream.ts Normal file
View file

@ -0,0 +1,103 @@
import {
createParser,
ParsedEvent,
ReconnectInterval,
} from 'eventsource-parser';
import { ChatMessage } from '@/sharedState';
export class OpenAIError extends Error {
type: string;
param: string;
code: string;
constructor(message: string, type: string, param: string, code: string) {
super(message);
this.name = 'OpenAIError';
this.type = type;
this.param = param;
this.code = code;
}
}
export const OpenAIStream = async (
model: string,
key: string,
messages: ChatMessage[],
) => {
const res = await fetch(`https://api.openai.com/v1/chat/completions`, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${key ? key : process.env.OPENAI_API_KEY}`,
...(process.env.OPENAI_ORGANIZATION && {
'OpenAI-Organization': process.env.OPENAI_ORGANIZATION,
}),
},
method: 'POST',
body: JSON.stringify({
model,
messages: [
{
role: 'system',
content: 'You are a helpful assistant.',
},
...messages,
],
max_tokens: 1000,
temperature: 1,
stream: true,
}),
});
const encoder = new TextEncoder();
const decoder = new TextDecoder();
if (res.status !== 200) {
const result = await res.json();
if (result.error) {
throw new OpenAIError(
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) {
const onParse = (event: ParsedEvent | ReconnectInterval) => {
if (event.type === 'event') {
const data = event.data;
if (data === '[DONE]') {
controller.close();
return;
}
try {
const json = JSON.parse(data);
const text = json.choices[0].delta.content;
const queue = encoder.encode(text);
controller.enqueue(queue);
} catch (e) {
controller.error(e);
}
}
};
const parser = createParser(onParse);
for await (const chunk of res.body as any) {
parser.feed(decoder.decode(chunk));
}
},
});
return stream;
};