UNPKG

dart-openai-sdk

Version:

OpenAI api-reference sdk

351 lines (324 loc) 15.4 kB
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; }; }