-
Notifications
You must be signed in to change notification settings - Fork 9.9k
/
Copy pathopenai.ts
150 lines (132 loc) · 3.67 KB
/
openai.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { IncomingMessage } from "http";
import {
ChatCompletionRequestMessageRoleEnum,
Configuration,
CreateChatCompletionResponse,
CreateCompletionRequest,
OpenAIApi,
} from "openai";
// This file contains utility functions for interacting with the OpenAI API
if (!process.env.OPENAI_API_KEY) {
throw new Error("Missing OPENAI_API_KEY environment variable");
}
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
export const openai = new OpenAIApi(configuration);
type CompletionOptions = Partial<CreateCompletionRequest> & {
prompt: string;
fallback?: string;
};
type EmbeddingOptions = {
input: string | string[];
model?: string;
};
export async function completion({
prompt,
fallback,
max_tokens,
temperature = 0,
model = "gpt-3.5-turbo", // use gpt-4 for better results
}: CompletionOptions) {
try {
// Note: this is not the proper way to use the ChatGPT conversational format, but it works for now
const messages = [
{
role: ChatCompletionRequestMessageRoleEnum.System,
content: prompt ?? "",
},
];
const result = await openai.createChatCompletion({
model,
messages,
temperature,
max_tokens: max_tokens ?? 800,
});
if (!result.data.choices[0].message) {
throw new Error("No text returned from completions endpoint");
}
return result.data.choices[0].message.content;
} catch (error) {
if (fallback) return fallback;
else throw error;
}
}
export async function* completionStream({
prompt,
fallback,
max_tokens = 800,
temperature = 0,
model = "gpt-3.5-turbo", // use gpt-4 for better results
}: CompletionOptions) {
try {
// Note: this is not the proper way to use the ChatGPT conversational format, but it works for now
const messages = [
{
role: ChatCompletionRequestMessageRoleEnum.System,
content: prompt ?? "",
},
];
const result = await openai.createChatCompletion(
{
model,
messages,
temperature,
max_tokens: max_tokens ?? 800,
stream: true,
},
{
responseType: "stream",
}
);
const stream = result.data as any as IncomingMessage;
let buffer = "";
const textDecoder = new TextDecoder();
for await (const chunk of stream) {
buffer += textDecoder.decode(chunk, { stream: true });
const lines = buffer.split("\n");
// Check if the last line is complete
if (buffer.endsWith("\n")) {
buffer = "";
} else {
buffer = lines.pop() || "";
}
for (const line of lines) {
const message = line.trim().split("data: ")[1];
if (message === "[DONE]") {
break;
}
// Check if the message is not undefined and a valid JSON string
if (message) {
try {
const data = JSON.parse(message) as CreateChatCompletionResponse;
// @ts-ignore
if (data.choices[0].delta?.content) {
// @ts-ignore
yield data.choices[0].delta?.content;
}
} catch (error) {
console.error("Error parsing JSON message:", error);
}
}
}
}
} catch (error) {
if (fallback) yield fallback;
else throw error;
}
}
export async function embedding({
input,
model = "text-embedding-ada-002",
}: EmbeddingOptions): Promise<number[][]> {
const result = await openai.createEmbedding({
model,
input,
});
if (!result.data.data[0].embedding) {
throw new Error("No embedding returned from the completions endpoint");
}
// Otherwise, return the embeddings
return result.data.data.map((d) => d.embedding);
}