UNPKG

nicechat

Version:

An extensible AI chat framework for OpenAi's models

230 lines (229 loc) 10.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const chalk_1 = __importDefault(require("chalk")); const fs_1 = __importDefault(require("fs")); const readline = __importStar(require("node:readline/promises")); const openai_1 = __importDefault(require("openai")); const path_1 = __importDefault(require("path")); const SETTINGS_FILE_NAME = ".nicechat.json"; const EXIT_COMMANDS = ["exit", "quit", "q", "bye"]; const engine = { plugins: {}, registerPlugin(p) { engine.plugins[p.meta.name] = p; }, run() { var _a; return __awaiter(this, void 0, void 0, function* () { const settings = yield readSettings(); const args = process.argv.slice(2); const command = (_a = args[0]) !== null && _a !== void 0 ? _a : "chat"; const openai = new openai_1.default({ apiKey: settings.openai_key }); const isDebug = args.some((x) => x === "--debug"); const functions = Object.values(engine.plugins).map((x) => x.meta); if (command === "list-models") { yield listModels(); } else if (command === "chat") { yield chat(); } else if (command === "help") { printHelp(); } else { console.log("Unknown command: " + command); } process.exit(0); function printHelp() { console.log("Available commands:"); const c = chalk_1.default.bold; console.log(` ${c("chat [--debug]")} start chat with the assistant (default)`); console.log(` ${c("list-models")} list available models`); console.log(` ${c("help")} show this help`); } function listModels() { return __awaiter(this, void 0, void 0, function* () { let models = null; while (models === null || models.hasNextPage()) { models = yield openai.models.list(); for (let m of models.data) { console.log(m.id); } } }); } function chat() { var _a, e_1, _b, _c; var _d, _e, _f, _g; return __awaiter(this, void 0, void 0, function* () { console.log("[" + chalk_1.default.blueBright(settings.system) + "]"); const history = [ system(settings.system), ]; // initial use input const input = yield readLine(); history.push(user(input)); while (true) { // starting stream const stream = yield openai.chat.completions.create({ model: settings.model, messages: history, functions, stream: true, }); let msg = ""; let fcall = { name: "", arguments: "", }; try { for (var _h = true, stream_1 = (e_1 = void 0, __asyncValues(stream)), stream_1_1; stream_1_1 = yield stream_1.next(), _a = stream_1_1.done, !_a; _h = true) { _c = stream_1_1.value; _h = false; const part = _c; // collect regular message const p = ((_e = (_d = part.choices[0]) === null || _d === void 0 ? void 0 : _d.delta) === null || _e === void 0 ? void 0 : _e.content) || ""; if (p) { process.stdout.write(chalk_1.default.greenBright(p)); msg += p; } // collect functino call const pfn = (_g = (_f = part.choices[0]) === null || _f === void 0 ? void 0 : _f.delta) === null || _g === void 0 ? void 0 : _g.function_call; if (pfn) { if (pfn.name) { fcall.name += pfn.name; } if (pfn.arguments) { fcall.arguments += pfn.arguments; } } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_h && !_a && (_b = stream_1.return)) yield _b.call(stream_1); } finally { if (e_1) throw e_1.error; } } stream.controller.abort(); if (fcall.name) { // function call history.push(assistantFn(fcall)); const plugin = engine.plugins[fcall.name]; if (!plugin) { throw new Error("Unregistered function: " + fcall.name); } const toolkit = { log: logger(`[${plugin.meta.name}]`), debug: isDebug ? logger(`[${plugin.meta.name}]`) : () => { }, }; const fnResult = yield plugin.execute(fcall.arguments, { toolkit }); console.log(); history.push(fnResultResp(fcall.name, fnResult)); } else { history.push(assistant(msg)); // ask user for next input console.log("\n"); const input = yield readLine(); history.push(user(input)); } } }); } }); }, }; function readSettings() { var _a; return __awaiter(this, void 0, void 0, function* () { // settings file should be in the HOME directory const absPath = path_1.default.join((_a = process.env.HOME) !== null && _a !== void 0 ? _a : "", SETTINGS_FILE_NAME); if (!fs_1.default.existsSync(absPath)) { console.log(`File ${chalk_1.default.bold(`~/${SETTINGS_FILE_NAME}`)} not found. Do you want to create it? ${chalk_1.default.yellowBright("[y]/n")}`); // TODO: read user input process.exit(0); } const settings = JSON.parse(fs_1.default.readFileSync(absPath, "utf-8")); return settings; }); } const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); function readLine() { return __awaiter(this, void 0, void 0, function* () { const textUntrimmed = yield rl.question(chalk_1.default.yellow("> ")); const text = textUntrimmed.trim(); if (EXIT_COMMANDS.includes(text)) { process.exit(0); } return text; }); } // helpers function system(content) { return { role: "system", content }; } function user(content) { return { role: "user", content }; } function assistant(content) { return { role: "assistant", content }; } function assistantFn(function_call) { return { role: "assistant", content: null, function_call }; } function fnResultResp(name, content) { return { role: "function", name, content }; } exports.default = engine; function logger(prefix) { return function log(msg) { console.log(`${chalk_1.default.blueBright(prefix)} ${msg}`); }; }