dart-openai-sdk
Version:
OpenAI api-reference sdk
351 lines (324 loc) • 15.4 kB
JavaScript
import http from 'node:http';
/**
* OpenAI 接口使用工具
* @param {string} key OpenAI API KEY (Find Key: https://platform.openai.com/account/api-keys)
* @param {string} organization OpenAI API Organization (Find Key: https://platform.openai.com/account/)
* @param {string} api_url https://api.openai.com/v1
* @param {number} max_cache_num 对话缓存的最大条数
* @param {boolean} debug 是否调试模式,会打印出一些日志信息
* @returns
*/
export default function OpenAI(key, organization, api_url = 'https://api.openai.com/v1', max_cache_num = 50, debug = false) {
if (!(this instanceof OpenAI)) return new OpenAI(key, api_url, debug);
var self = this;
if (key) {
self.key = key;
} else {
throw new Error('[OpenAI INIT METHOD] Can not find key from params');
}
if (organization) {
self.organization = organization;
} else {
throw new Error('[OpenAI INIT METHOD] Can not find organization from params');
}
self.debug = debug;
self.max_cache_num = max_cache_num;
self.api_url = api_url;
self.log = (msg1, msg2) => {
if (self.debug) {
console.log("==-[DEBUG-INFO]-========================================");
msg2 ? console.log(msg1, msg2) : console.log(msg1);
}
};
self.chat_cache = {};
self.last_clear_cache_time = 0;
self.clear_cache_timeout = async () => {
self.log("call clear_cache_timeout");
let clear_time = new Date().getTime() - (1000 * 10);
if (self.last_clear_cache_time > clear_time) {
return;
}
self.last_clear_cache_time = new Date().getTime();
for (let key in self.chat_cache) {
let obj = self.chat_cache[key];
let ot = new Date().getTime() - (1000 * 60 * 30);
if ((obj.time || 0) < ot) {
self.chat_cache[key] = null;
}
}
}
self.get_chat_cache = (user) => {
self.log("call get_chat_cache");
self.clear_cache_timeout();
if (!user) {
return [];
}
let obj = self.chat_cache[user];
if (!obj) {
return [];
}
let chats = obj.chats || [];
return JSON.parse(JSON.stringify(chats));
}
self.put_chat_cache = (user, role, message) => {
self.log("call put_chat_cache");
self.clear_cache_timeout();
if (!user || !role || !message) {
return;
}
let obj = self.chat_cache[user];
if (obj == null || obj == undefined) {
obj = {};
obj.user = user;
obj.chats = [];
}
obj.time = new Date().getTime();
let chats = obj.chats;
if (chats.length > self.max_cache_num * 2) {
chats.splice(0, 1);
}
chats.push({
"role": role,
"content": message
});
obj.chats = chats;
self.chat_cache[user] = obj;
}
/**
* 列出并描述API中可用的各种模型。你可以参考模型文档来了解有哪些模型可用以及它们之间的区别。
* List and describe the various models available in the API. You can refer to the Models documentation to understand what models are available and the differences between them.
*/
self.models = async () => {
self.log("call models");
return await self.request('/models', null, true);
};
/**
* 检索一个模型实例,提供关于模型的基本信息,如所有者和许可。
* Retrieves a model instance, providing basic information about the model such as the owner and permissioning.
* @param {string} model 这个请求要使用的模型的ID。(The ID of the model to use for this request)
* @returns
*/
self.retrieveModel = async (model) => {
self.log("call retrieveModel");
if (!model) {
throw new Error('[OpenAI RETRIEVE MODEL METHOD] Please pass in the correct model ID');
}
return await self.request('/models/' + model, null, true);
};
/**
* 为提供的提示和参数完成创建
* @link https://platform.openai.com/docs/api-reference/completions/create
* @param {Object} param 参数,请参考文档 {
* 'model':'ID of the model to use. You can use the List models API to see all of your available models, or see our Model overview for descriptions of them.',
* 'prompt':'The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays',
* 'suffix':'The suffix that comes after a completion of inserted text.',
* 'max_tokens':'The maximum number of tokens to generate in the completion.',
* 'temperature':'What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.',
* 'top_p':'An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.',
* 'n':'How many completions to generate for each prompt.',
* 'stream':'Whether to stream back partial progress. If set, tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a data: [DONE] message.',
* 'logprobs':'Include the log probabilities on the logprobs most likely tokens, as well the chosen tokens. For example, if logprobs is 5, the API will return a list of the 5 most likely tokens. The API will always return the logprob of the sampled token, so there may be up to logprobs+1 elements in the response.',
* 'echo':'Echo back the prompt in addition to the completion',
* 'stop':'Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.',
* 'presence_penalty':'Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.',
* 'frequency_penalty':'Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.',
* 'best_of':'Generates best_of completions server-side and returns the "best" (the one with the highest log probability per token). Results cannot be streamed.',
* 'logit_bias':'Modify the likelihood of specified tokens appearing in the completion.',
* 'user':'A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. '
* }
* @returns
*/
self.completions = async (param) => {
self.log("call completions");
if (!param.model) {
throw new Error('[OpenAI COMPLETIONS METHOD] Please pass in the correct model ID');
}
return await self.request('/completions', param);
};
/**
* 为提供的提示和参数完成创建
* @link https://platform.openai.com/docs/api-reference/completions/create
* @param {string} model 要使用的模型的 ID
* @param {any} prompt 生成完成的提示,编码为字符串、字符串数组、标记数组或标记数组数组。
* @param {number} max_tokens 最大令牌数
* @param {string} user 代表您的最终用户的唯一标识符,可以帮助 OpenAI 监控和检测滥用行为。
* @returns
*/
self.completionsB = async (model, prompt, max_tokens, user) => {
self.log("call completionsB");
if (!model) {
throw new Error('[OpenAI COMPLETIONSB METHOD] Please pass in the correct model ID');
}
return await self.request('/completions', {
model, prompt, max_tokens, user
});
};
/**
* Creates a completion for the chat message
* @link https://platform.openai.com/docs/api-reference/chat/create
* @param {string} message 对话消息
* @param {string} user 用户ID
* @param {string} model 要使用的模型的 ID
* @returns
*/
self.chat = async (message, user, model = 'gpt-3.5-turbo') => {
self.log("call chat");
if (!message) {
throw new Error('[OpenAI CHAT COMPLETIONS METHOD] Please pass in the messages');
}
if (!user) {
throw new Error('[OpenAI CHAT COMPLETIONS METHOD] Please pass in the user');
}
let chats = self.get_chat_cache(user);
chats.push({
"role": "user",
"content": message
});
self.log("chatCompletions", chats);
let response = await self.request('/chat/completions', {
model, user, messages: chats
});
let choices = response.choices;
if (choices && choices.length > 0) {
let msg = choices[0].message;
self.put_chat_cache(user, 'user', message);
self.put_chat_cache(user, msg.role, msg.content);
}
return response;
};
/**
* Creates a completion for the chat message
* @link https://platform.openai.com/docs/api-reference/chat/create
* @param {string} message 对话消息
* @param {string} user 用户ID
* @param {string} model 要使用的模型的 ID
* @returns
*/
self.chatByStream = async (message, user, model = 'gpt-3.5-turbo') => {
self.log("call chat");
if (!message) {
throw new Error('[OpenAI CHAT COMPLETIONS METHOD] Please pass in the messages');
}
if (!user) {
throw new Error('[OpenAI CHAT COMPLETIONS METHOD] Please pass in the user');
}
let chats = self.get_chat_cache(user);
chats.push({
"role": "user",
"content": message
});
self.log("chatCompletions", chats);
let response = await self.request('/chat/completions', {
model, user, messages: chats, stream: true
}, false, true);
let choices = response.choices;
if (choices && choices.length > 0) {
let msg = choices[0].message;
self.put_chat_cache(user, 'user', message);
self.put_chat_cache(user, msg.role, msg.content);
}
return response;
};
/**
* 根据提示创建一个图像。
* @param {string} prompt 对所需图像的文字描述。最大长度为1000个字符。
* @param {string} user 用户ID
* @param {number} number [1 ~ 10] 要生成的图像的数量。必须在1到10之间。
* @param {string} size 生成图像的大小。必须是其中之一 ['256x256','512x512','1024x1024']
* @param {string} response_format 返回生成的图像的格式。必须是以下之一 ['url','b64_json']
* @returns
*/
self.image = async (prompt, user, number = 1, size = '512x512', response_format = 'b64_json') => {
self.log("call image");
if (!prompt) {
throw new Error('[OpenAI IMAGE METHOD] Please pass in the prompt');
}
if (!user) {
throw new Error('[OpenAI IMAGE METHOD] Please pass in the user');
}
let response = await self.request('/images/generations', {
prompt, user, size, response_format, "n": number
});
return response;
}
self.request = async (api, body, get = false, stream = false) => {
self.log("call request");
let url = self.api_url + api;
let options = {
method: get ? 'GET' : 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + self.key,
'OpenAI-Organization': self.organization
}
}
self.log("call request stream(" + stream + ") by url " + url, options);
let start_time = new Date().getTime();
let response = await new Promise((resolve, reject) => {
let req = http.request(url, options, async (res) => {
res.setEncoding('utf-8');
let bodys = [];
res.on('data', (chunk) => {
if (chunk == null || chunk == undefined || chunk.trim() == '') {
return;
}
bodys.push(chunk);
});
res.on('end', () => {
let bodyString = bodys.join();
var json;
if (stream) {
let obj = {};
obj.object = 'chat.completion';
let content = '';
let cks = bodyString.replace(',,', ',').split(/\n+/g) || [];
for (let ck of cks) {
if (ck == null || ck == undefined || ck.trim() == '') {
continue;
}
ck = ck.trim();
if ("data: [DONE]" != ck && ck.startsWith('data:')) {
try {
let json = JSON.parse(ck.substring(5));
obj.id = json.id;
obj.created = json.created;
obj.model = json.model;
let delta = json.choices[0].delta;
if (delta) {
content += (delta.content || '');
}
} catch (e) {
console.error("Parse chunk error : " + ck);
}
}
}
obj.choices = [{
"message": {
"role": "assistant",
"content": content
},
"finish_reason": "stop",
"index": 0
}];
json = obj;
} else {
json = JSON.parse(bodyString);
}
resolve(json);
});
});
req.on('error', function (e) {
reject(e);
});
if (body) {
req.write(JSON.stringify(body));
} else {
req.write('');
}
req.end();
}).then(res => res);
let end_time = new Date().getTime();
self.log("call request response: (cost " + (end_time - start_time) + " ms)", response);
return response;
};
}