@proteinjs/conversation 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +21 -0
  3. package/dist/index.js +27 -0
  4. package/dist/jest.config.js +10 -0
  5. package/dist/src/CodegenConversation.js +120 -0
  6. package/dist/src/Conversation.js +193 -0
  7. package/dist/src/ConversationModule.js +2 -0
  8. package/dist/src/Function.js +2 -0
  9. package/dist/src/OpenAi.js +209 -0
  10. package/dist/src/Paragraph.js +18 -0
  11. package/dist/src/Sentence.js +22 -0
  12. package/dist/src/code_template/Code.js +41 -0
  13. package/dist/src/code_template/CodeTemplate.js +39 -0
  14. package/dist/src/code_template/CodeTemplateModule.js +46 -0
  15. package/dist/src/code_template/Repo.js +127 -0
  16. package/dist/src/fs/conversation_fs/ConversationFsModerator.js +99 -0
  17. package/dist/src/fs/conversation_fs/ConversationFsModule.js +68 -0
  18. package/dist/src/fs/conversation_fs/FsFunctions.js +256 -0
  19. package/dist/src/fs/git/GitModule.js +45 -0
  20. package/dist/src/fs/keyword_to_files_index/KeywordToFilesIndexFunctions.js +65 -0
  21. package/dist/src/fs/keyword_to_files_index/KeywordToFilesIndexModule.js +89 -0
  22. package/dist/src/fs/package/PackageFunctions.js +214 -0
  23. package/dist/src/fs/package/PackageModule.js +102 -0
  24. package/dist/src/history/MessageHistory.js +44 -0
  25. package/dist/src/history/MessageModerator.js +2 -0
  26. package/dist/src/template/ConversationTemplate.js +2 -0
  27. package/dist/src/template/ConversationTemplateFunctions.js +54 -0
  28. package/dist/src/template/ConversationTemplateModule.js +80 -0
  29. package/dist/src/template/createApp/CreateAppTemplate.js +40 -0
  30. package/dist/src/template/createCode/CreateCodeConversationTemplate.js +51 -0
  31. package/dist/src/template/createPackage/CreatePackageConversationTemplate.js +54 -0
  32. package/dist/src/template/createPackage/jest.config.js +10 -0
  33. package/dist/src/template/createPackage/tsconfig.json +13 -0
  34. package/dist/test/createKeywordFilesIndex.test.js +17 -0
  35. package/dist/test/openai/openai.generateList.test.js +16 -0
  36. package/dist/test/openai/openai.parseCodeFromMarkdown.test.js +18 -0
  37. package/dist/test/repo/repo.test.js +29 -0
  38. package/dist/test/setup.js +1 -0
  39. package/index.ts +11 -0
  40. package/jest.config.js +9 -0
  41. package/package.json +34 -0
  42. package/src/CodegenConversation.ts +92 -0
  43. package/src/Conversation.ts +207 -0
  44. package/src/ConversationModule.ts +13 -0
  45. package/src/Function.ts +8 -0
  46. package/src/OpenAi.ts +212 -0
  47. package/src/Paragraph.ts +17 -0
  48. package/src/Sentence.ts +20 -0
  49. package/src/code_template/Code.ts +53 -0
  50. package/src/code_template/CodeTemplate.ts +39 -0
  51. package/src/code_template/CodeTemplateModule.ts +50 -0
  52. package/src/code_template/Repo.ts +156 -0
  53. package/src/fs/conversation_fs/ConversationFsModerator.ts +121 -0
  54. package/src/fs/conversation_fs/ConversationFsModule.ts +64 -0
  55. package/src/fs/conversation_fs/FsFunctions.ts +253 -0
  56. package/src/fs/git/GitModule.ts +39 -0
  57. package/src/fs/keyword_to_files_index/KeywordToFilesIndexFunctions.ts +55 -0
  58. package/src/fs/keyword_to_files_index/KeywordToFilesIndexModule.ts +90 -0
  59. package/src/fs/package/PackageFunctions.ts +210 -0
  60. package/src/fs/package/PackageModule.ts +106 -0
  61. package/src/history/MessageHistory.ts +57 -0
  62. package/src/history/MessageModerator.ts +6 -0
  63. package/src/template/ConversationTemplate.ts +12 -0
  64. package/src/template/ConversationTemplateFunctions.ts +43 -0
  65. package/src/template/ConversationTemplateModule.ts +83 -0
  66. package/src/template/createApp/CreateAppTemplate.ts +33 -0
  67. package/src/template/createCode/CreateCodeConversationTemplate.ts +41 -0
  68. package/src/template/createPackage/CreatePackageConversationTemplate.ts +42 -0
  69. package/src/template/createPackage/jest.config.js +9 -0
  70. package/src/template/createPackage/tsconfig.json +13 -0
  71. package/test/createKeywordFilesIndex.test.ts +7 -0
  72. package/test/openai/openai.generateList.test.ts +6 -0
  73. package/test/openai/openai.parseCodeFromMarkdown.test.ts +20 -0
  74. package/test/repo/repo.test.ts +33 -0
  75. package/test/setup.js +0 -0
  76. package/tsconfig.json +109 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ ## 1.0.1 (2024-04-19)
7
+
8
+ **Note:** Version bump only for package @proteinjs/conversation
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Brent Bahry
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./src/Sentence"), exports);
18
+ __exportStar(require("./src/Paragraph"), exports);
19
+ __exportStar(require("./src/OpenAi"), exports);
20
+ __exportStar(require("./src/code_template/CodeTemplate"), exports);
21
+ __exportStar(require("./src/Conversation"), exports);
22
+ __exportStar(require("./src/CodegenConversation"), exports);
23
+ __exportStar(require("./src/code_template/Code"), exports);
24
+ __exportStar(require("./src/ConversationModule"), exports);
25
+ __exportStar(require("./src/Function"), exports);
26
+ __exportStar(require("./src/history/MessageModerator"), exports);
27
+ __exportStar(require("./src/history/MessageHistory"), exports);
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ transform: {
6
+ '^.+\\.tsx?$': 'ts-jest',
7
+ },
8
+ testMatch: ["**/?(*.)+(spec|test).ts"],
9
+ setupFiles: ['./test/setup'],
10
+ };
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.CodegenConversation = void 0;
36
+ const readline = __importStar(require("readline-sync"));
37
+ const Conversation_1 = require("./Conversation");
38
+ const KeywordToFilesIndexModule_1 = require("./fs/keyword_to_files_index/KeywordToFilesIndexModule");
39
+ const ConversationTemplateModule_1 = require("./template/ConversationTemplateModule");
40
+ const ConversationFsModule_1 = require("./fs/conversation_fs/ConversationFsModule");
41
+ const PackageModule_1 = require("./fs/package/PackageModule");
42
+ const util_1 = require("@proteinjs/util");
43
+ const GitModule_1 = require("./fs/git/GitModule");
44
+ class CodegenConversation {
45
+ constructor(repoPath) {
46
+ this.repoPath = repoPath;
47
+ }
48
+ start() {
49
+ return __awaiter(this, void 0, void 0, function* () {
50
+ const conversation = yield this.createConversation();
51
+ conversation.addAssistantMessagesToHistory([CodegenConversation.INITIAL_QUESTION]);
52
+ const initialUserInput = this.respondToUser(CodegenConversation.INITIAL_QUESTION);
53
+ let response = yield conversation.generateResponse([initialUserInput], CodegenConversation.MODEL);
54
+ while (true) {
55
+ const userInput = this.respondToUser(response);
56
+ response = yield conversation.generateResponse([userInput], CodegenConversation.MODEL);
57
+ }
58
+ });
59
+ }
60
+ createConversation() {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ const conversation = new Conversation_1.Conversation({
63
+ name: this.constructor.name,
64
+ modules: yield this.getModules(),
65
+ logLevel: 'info',
66
+ });
67
+ conversation.addSystemMessagesToHistory(this.getSystemMessages());
68
+ return conversation;
69
+ });
70
+ }
71
+ getModules() {
72
+ return __awaiter(this, void 0, void 0, function* () {
73
+ const moduleFactories = [
74
+ new ConversationFsModule_1.ConversationFsModuleFactory(),
75
+ new KeywordToFilesIndexModule_1.KeywordToFilesIndexModuleFactory(),
76
+ new PackageModule_1.PackageModuleFactory(),
77
+ new ConversationTemplateModule_1.ConversationTemplateModuleFactory(),
78
+ new GitModule_1.GitModuleFactory(),
79
+ ];
80
+ const modules = [];
81
+ for (let moduleFactory of moduleFactories)
82
+ modules.push(yield moduleFactory.createModule(this.repoPath));
83
+ return modules;
84
+ });
85
+ }
86
+ getSystemMessages() {
87
+ return [
88
+ `We are going to have a conversation with the user to generate code`,
89
+ `Await all function calls that return a promise`,
90
+ `Try to repspond to the user with as few words as possible while still having a conversational tone`,
91
+ `When generating code, export the objects you create inline; do not use 'export default' syntax`,
92
+ // `After finding a file to work with, assume the user's following question pertains to that file and use ${readFilesFunctionName} to read the file if needed`,
93
+ // `If a conversation summary exists, if you aren't already working with a file, use the most relevant keyword mentioned in the conversation summary to find a file to read (using the ${searchFilesFunctionName} function) and then respond to the user after reading the file`,
94
+ // `Use the most relevant keyword mentioned in the conversation summary to find a file to read (using the ${searchFilesFunctionName} function) and then respond to the user after reading the file`,
95
+ // `If the conversation summary indicates the user was working with a file get the file path (use the ${searchFilesFunctionName} function if needed) and read the file with the ${readFilesFunctionName} function. Use that file as context to respond to the user.`,
96
+ //
97
+ // `Use the conversation summary to identify a file as context for the user interaction`,
98
+ // `Use the ${searchFilesFunctionName} function to find the file if needed; read the file if needed`,
99
+ // `If the user is referring to a function, object, class, or type and you don't have the relevant file content, first inspect the conversation summary in the chat history (if it exists) to find a file name, and call the ${searchFilesFunctionName} function and read the file before responding to the user`,
100
+ // `Before calling ${searchFilesFunctionName}, ${searchLibrariesFunctionName} or ${searchPackagesFunctionName}, use the conversation summary in the chat history to identify a file or keyword to search for instead; after reading that file, respond to the user's request`,
101
+ //
102
+ // `Use the ${getRecentlyAccessedFilePathsFunctionName} function find a file that might pertain to the user's request before searching files, libraries or packages; read that file then respond to the user`,
103
+ // `When trying to locate code, use the ${getRecentlyAccessedFilePathsFunctionName} function to search recently accessed files first, then proceed to calling other functions: ${searchLibrariesFunctionName}, ${searchPackagesFunctionName}, ${searchFilesFunctionName}`,
104
+ // `The conversation summary indicates files recently worked in as well`,
105
+ // `If that doesn't yield results, proceed to calling the ${searchLibrariesFunctionName} function, then fall back to functions: ${searchPackagesFunctionName}, ${searchFilesFunctionName}`,
106
+ //
107
+ // `To find code, a file, or a library, call ${getRecentlyAccessedFilePathsFunctionName} and read the most recent file, after trying that call ${searchLibrariesFunctionName} then ${searchFilesFunctionName} to find a relevant file`,
108
+ // `The file mentioned in the conversation summary should be read if we're not already working in a file`,
109
+ // `If there is a conversation summary assistant message, use that to pick a file to read before responding to the user if not already working with a specific file`,
110
+ // `Check for a previous conversation summary assistant message in the chat history; if there is one and it mentions a file the user was working with, call ${searchLibrariesFunctionName} to find the file path then call ${readFilesFunctionName} to read the file. Do this to build context before responding to the user`,
111
+ ];
112
+ }
113
+ respondToUser(message) {
114
+ return readline.question(`${util_1.textColorMap.cyan}[${CodegenConversation.BOT_NAME}] ${message}${util_1.Reset}\n`);
115
+ }
116
+ }
117
+ exports.CodegenConversation = CodegenConversation;
118
+ CodegenConversation.INITIAL_QUESTION = 'What would you like to create?';
119
+ CodegenConversation.BOT_NAME = 'Alina';
120
+ CodegenConversation.MODEL = 'gpt-4'; //'gpt-3.5-turbo-16k';
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.summarizeConversationHistoryFunction = exports.summarizeConversationHistoryFunctionName = exports.Conversation = void 0;
13
+ const OpenAi_1 = require("./OpenAi");
14
+ const MessageHistory_1 = require("./history/MessageHistory");
15
+ const util_1 = require("@proteinjs/util");
16
+ const util_node_1 = require("@proteinjs/util-node");
17
+ const tiktoken_1 = require("tiktoken");
18
+ const PackageFunctions_1 = require("./fs/package/PackageFunctions");
19
+ class Conversation {
20
+ constructor(params) {
21
+ var _a, _b, _c, _d;
22
+ this.tokenLimit = 3000;
23
+ this.systemMessages = [];
24
+ this.functions = [];
25
+ this.messageModerators = [];
26
+ this.generatedCode = false;
27
+ this.generatedList = false;
28
+ this.params = params;
29
+ this.history = new MessageHistory_1.MessageHistory({ maxMessages: (_a = params.limits) === null || _a === void 0 ? void 0 : _a.maxMessagesInHistory, enforceMessageLimit: (_b = params.limits) === null || _b === void 0 ? void 0 : _b.enforceLimits });
30
+ this.logger = new util_1.Logger(params.name, params.logLevel);
31
+ if (params.modules)
32
+ this.addModules(params.modules);
33
+ if (typeof ((_c = params.limits) === null || _c === void 0 ? void 0 : _c.enforceLimits) === 'undefined' || params.limits.enforceLimits) {
34
+ this.addFunctions('Conversation', [
35
+ (0, exports.summarizeConversationHistoryFunction)(this),
36
+ ]);
37
+ }
38
+ if ((_d = params.limits) === null || _d === void 0 ? void 0 : _d.tokenLimit)
39
+ this.tokenLimit = params.limits.tokenLimit;
40
+ }
41
+ addModules(modules) {
42
+ for (let module of modules) {
43
+ if (module.getSystemMessages().length < 1)
44
+ continue;
45
+ this.addSystemMessagesToHistory([
46
+ `The following are instructions from the ${module.getName()} module: ${module.getSystemMessages().join('. ')}`,
47
+ ]);
48
+ this.addFunctions(module.getName(), module.getFunctions());
49
+ this.addMessageModerators(module.getMessageModerators());
50
+ }
51
+ }
52
+ addFunctions(moduleName, functions) {
53
+ this.functions.push(...functions);
54
+ let functionInstructions = `The following are instructions from functions in the ${moduleName} module:`;
55
+ let functionInstructionsAdded = false;
56
+ for (let f of functions) {
57
+ if (f.instructions) {
58
+ if (!f.instructions || f.instructions.length < 1)
59
+ continue;
60
+ functionInstructionsAdded = true;
61
+ const instructionsParagraph = f.instructions.join('. ');
62
+ functionInstructions += ` ${f.definition.name}: ${instructionsParagraph}.`;
63
+ }
64
+ }
65
+ if (!functionInstructionsAdded)
66
+ return;
67
+ this.addSystemMessagesToHistory([functionInstructions]);
68
+ }
69
+ addMessageModerators(messageModerators) {
70
+ this.messageModerators.push(...messageModerators);
71
+ }
72
+ enforceTokenLimit(messages, model) {
73
+ var _a;
74
+ return __awaiter(this, void 0, void 0, function* () {
75
+ if (((_a = this.params.limits) === null || _a === void 0 ? void 0 : _a.enforceLimits) === false)
76
+ return;
77
+ const resolvedModel = model ? model : OpenAi_1.DEFAULT_MODEL;
78
+ const encoder = (0, tiktoken_1.encoding_for_model)(resolvedModel);
79
+ const conversation = this.history.toString() + messages.join('. ');
80
+ const encoded = encoder.encode(conversation);
81
+ console.log(`current tokens: ${encoded.length}`);
82
+ if (encoded.length < this.tokenLimit)
83
+ return;
84
+ const summarizeConversationRequest = `First, call the ${exports.summarizeConversationHistoryFunctionName} function`;
85
+ yield OpenAi_1.OpenAi.generateResponse([summarizeConversationRequest], model, this.history, this.functions, this.messageModerators, this.params.logLevel);
86
+ const referenceSummaryRequest = `If there's a file mentioned in the conversation summary, find and read the file to better respond to my next request. If that doesn't find anything, call the ${PackageFunctions_1.searchLibrariesFunctionName} function on other keywords in the conversation summary to find a file to read`;
87
+ yield OpenAi_1.OpenAi.generateResponse([referenceSummaryRequest], model, this.history, this.functions, this.messageModerators, this.params.logLevel);
88
+ });
89
+ }
90
+ summarizeConversationHistory(summary) {
91
+ this.clearHistory();
92
+ this.history.push([{ role: 'assistant', content: `Previous conversation summary: ${summary}` }]);
93
+ }
94
+ clearHistory() {
95
+ this.history = new MessageHistory_1.MessageHistory();
96
+ this.history.push(this.systemMessages);
97
+ }
98
+ addSystemMessagesToHistory(messages, unshift = false) {
99
+ const chatCompletions = messages.map(message => { return { role: 'system', content: message }; });
100
+ if (unshift) {
101
+ this.history.getMessages().unshift(...chatCompletions);
102
+ this.history.prune();
103
+ this.systemMessages.unshift(...chatCompletions);
104
+ }
105
+ else {
106
+ this.history.push(chatCompletions);
107
+ this.systemMessages.push(...chatCompletions);
108
+ }
109
+ }
110
+ addAssistantMessagesToHistory(messages, unshift = false) {
111
+ const chatCompletions = messages.map(message => { return { role: 'assistant', content: message }; });
112
+ if (unshift) {
113
+ this.history.getMessages().unshift(...chatCompletions);
114
+ this.history.prune();
115
+ }
116
+ else
117
+ this.history.push(chatCompletions);
118
+ }
119
+ addUserMessagesToHistory(messages, unshift = false) {
120
+ const chatCompletions = messages.map(message => { return { role: 'user', content: message }; });
121
+ if (unshift) {
122
+ this.history.getMessages().unshift(...chatCompletions);
123
+ this.history.prune();
124
+ }
125
+ else
126
+ this.history.push(chatCompletions);
127
+ }
128
+ generateResponse(messages, model) {
129
+ return __awaiter(this, void 0, void 0, function* () {
130
+ yield this.enforceTokenLimit(messages, model);
131
+ return yield OpenAi_1.OpenAi.generateResponse(messages, model, this.history, this.functions, this.messageModerators, this.params.logLevel);
132
+ });
133
+ }
134
+ generateCode(description, model) {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ this.logger.info(`Generating code for description:\n${description.join('\n')}`);
137
+ const code = yield OpenAi_1.OpenAi.generateCode(description, model, this.history, this.functions, this.messageModerators, !this.generatedCode, this.params.logLevel);
138
+ this.logger.info(`Generated code:\n${code.slice(0, 150)}${code.length > 150 ? '...' : ''}`);
139
+ this.generatedCode = true;
140
+ return code;
141
+ });
142
+ }
143
+ updateCodeFromFile(codeToUpdateFilePath, dependencyCodeFilePaths, description, model) {
144
+ return __awaiter(this, void 0, void 0, function* () {
145
+ const codeToUpdate = yield util_node_1.Fs.readFile(codeToUpdateFilePath);
146
+ let dependencyDescription = `Assume the following exists:\n`;
147
+ for (let dependencyCodeFilePath of dependencyCodeFilePaths) {
148
+ const dependencCode = yield util_node_1.Fs.readFile(dependencyCodeFilePath);
149
+ dependencyDescription += dependencCode + '\n\n';
150
+ }
151
+ this.logger.info(`Updating code from file: ${codeToUpdateFilePath}`);
152
+ return yield this.updateCode(codeToUpdate, dependencyDescription + description, model);
153
+ });
154
+ }
155
+ updateCode(code, description, model) {
156
+ return __awaiter(this, void 0, void 0, function* () {
157
+ this.logger.info(`Updating code:\n${code.slice(0, 150)}${code.length > 150 ? '...' : ''}\nFrom description: ${description}`);
158
+ const updatedCode = yield OpenAi_1.OpenAi.updateCode(code, description, model, this.history, this.functions, this.messageModerators, !this.generatedCode, this.params.logLevel);
159
+ this.logger.info(`Updated code:\n${updatedCode.slice(0, 150)}${updatedCode.length > 150 ? '...' : ''}`);
160
+ this.generatedCode = true;
161
+ return updatedCode;
162
+ });
163
+ }
164
+ generateList(description, model) {
165
+ return __awaiter(this, void 0, void 0, function* () {
166
+ const list = yield OpenAi_1.OpenAi.generateList(description, model, this.history, this.functions, this.messageModerators, !this.generatedList, this.params.logLevel);
167
+ this.generatedList = true;
168
+ return list;
169
+ });
170
+ }
171
+ }
172
+ exports.Conversation = Conversation;
173
+ exports.summarizeConversationHistoryFunctionName = 'summarizeConversationHistory';
174
+ const summarizeConversationHistoryFunction = (conversation) => {
175
+ return {
176
+ definition: {
177
+ name: exports.summarizeConversationHistoryFunctionName,
178
+ description: 'Clear the conversation history and summarize what was in it',
179
+ parameters: {
180
+ type: 'object',
181
+ properties: {
182
+ summary: {
183
+ type: 'string',
184
+ description: 'A 1-3 sentence summary of the current chat history',
185
+ },
186
+ },
187
+ required: ['summary']
188
+ },
189
+ },
190
+ call: (params) => __awaiter(void 0, void 0, void 0, function* () { return conversation.summarizeConversationHistory(params.summary); }),
191
+ };
192
+ };
193
+ exports.summarizeConversationHistoryFunction = summarizeConversationHistoryFunction;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.OpenAi = exports.DEFAULT_MODEL = void 0;
13
+ const openai_1 = require("openai");
14
+ const util_1 = require("@proteinjs/util");
15
+ const MessageHistory_1 = require("./history/MessageHistory");
16
+ function delay(ms) {
17
+ return new Promise(resolve => setTimeout(resolve, ms));
18
+ }
19
+ exports.DEFAULT_MODEL = 'gpt-3.5-turbo';
20
+ class OpenAi {
21
+ static generateResponse(messages, model, history, functions, messageModerators, logLevel = 'info') {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ const logger = new util_1.Logger('OpenAi.generateResponse', logLevel);
24
+ const messageParams = messages.map(message => { return { role: 'user', content: message }; });
25
+ if (history)
26
+ history.push(messageParams);
27
+ let messageParamsWithHistory = history ? history : new MessageHistory_1.MessageHistory().push(messageParams);
28
+ if (messageModerators)
29
+ messageParamsWithHistory = OpenAi.moderateHistory(messageParamsWithHistory, messageModerators);
30
+ const response = yield OpenAi.executeRequest(messageParamsWithHistory, logLevel, functions, model);
31
+ const responseMessage = response.choices[0].message;
32
+ if (responseMessage.function_call) {
33
+ messageParamsWithHistory.push([responseMessage]);
34
+ const functionReturnMessage = yield this.callFunction(logLevel, responseMessage.function_call, functions);
35
+ if (functionReturnMessage)
36
+ messageParamsWithHistory.push([functionReturnMessage]);
37
+ return yield this.generateResponse([], model, messageParamsWithHistory, functions, messageModerators, logLevel);
38
+ }
39
+ const responseText = responseMessage.content;
40
+ if (!responseText) {
41
+ logger.error(`Received response: ${JSON.stringify(response)}`);
42
+ throw new Error(`Response was empty for messages: ${messages.join('\n')}`);
43
+ }
44
+ messageParamsWithHistory.push([responseMessage]);
45
+ return responseText;
46
+ });
47
+ }
48
+ static moderateHistory(history, messageModerators) {
49
+ for (let messageModerator of messageModerators)
50
+ history.setMessages(messageModerator.observe(history.getMessages()));
51
+ return history;
52
+ }
53
+ static executeRequest(messageParamsWithHistory, logLevel, functions, model) {
54
+ return __awaiter(this, void 0, void 0, function* () {
55
+ const logger = new util_1.Logger('OpenAi.executeRequest', logLevel);
56
+ const openaiApi = new openai_1.OpenAI();
57
+ let response;
58
+ try {
59
+ const latestMessage = messageParamsWithHistory.getMessages()[messageParamsWithHistory.getMessages().length - 1];
60
+ if (latestMessage.content)
61
+ logger.info(`Sending request: ${latestMessage.content}`);
62
+ else if (latestMessage.role == 'function')
63
+ logger.info(`Sending request: returning output of ${latestMessage.name} function`);
64
+ else
65
+ logger.info(`Sending request`);
66
+ logger.debug(`Sending messages: ${JSON.stringify(messageParamsWithHistory.getMessages(), null, 2)}`, true);
67
+ response = yield openaiApi.chat.completions.create({
68
+ model: model ? model : exports.DEFAULT_MODEL,
69
+ temperature: 0,
70
+ messages: messageParamsWithHistory.getMessages(),
71
+ functions: functions === null || functions === void 0 ? void 0 : functions.map(f => f.definition),
72
+ });
73
+ const responseMessage = response.choices[0].message;
74
+ if (responseMessage.content)
75
+ logger.info(`Received response: ${responseMessage.content}`);
76
+ else if (responseMessage.function_call)
77
+ logger.info(`Received response: call ${responseMessage.function_call.name} function`);
78
+ else
79
+ logger.info(`Received response`);
80
+ if (response.usage)
81
+ logger.info(JSON.stringify(response.usage));
82
+ else
83
+ logger.info(JSON.stringify(`Usage data missing`));
84
+ }
85
+ catch (error) {
86
+ logger.info(`Received error response, error type: ${error.type}`);
87
+ if (typeof error.status !== 'undefined' && error.status == 429) {
88
+ if (error.type == 'tokens' && typeof error.headers['x-ratelimit-reset-tokens'] === 'string') {
89
+ const waitTime = parseInt(error.headers['x-ratelimit-reset-tokens']);
90
+ const remainingTokens = error.headers['x-ratelimit-remaining-tokens'];
91
+ const delayMs = 15000;
92
+ logger.warn(`Waiting to retry in ${delayMs / 1000}s, token reset in: ${waitTime}s, remaining tokens: ${remainingTokens}`);
93
+ yield delay(delayMs);
94
+ return yield OpenAi.executeRequest(messageParamsWithHistory, logLevel, functions, model);
95
+ }
96
+ }
97
+ throw error;
98
+ }
99
+ return response;
100
+ });
101
+ }
102
+ static callFunction(logLevel, functionCall, functions) {
103
+ return __awaiter(this, void 0, void 0, function* () {
104
+ const logger = new util_1.Logger('OpenAi.callFunction', logLevel);
105
+ if (!functions) {
106
+ const warning = `Assistant attempted to call a function when no functions were provided`;
107
+ logger.warn(warning);
108
+ const message = { role: 'user', content: warning };
109
+ return message;
110
+ }
111
+ functionCall.name = functionCall.name.split('.').pop();
112
+ const f = functions.find(f => f.definition.name === functionCall.name);
113
+ if (!f) {
114
+ const warning = `Assistant attempted to call nonexistent function: ${functionCall.name}`;
115
+ logger.warn(warning);
116
+ const message = { role: 'user', content: warning };
117
+ return message;
118
+ }
119
+ let returnObject = null;
120
+ try {
121
+ logger.info(`Assistant calling function: ${f.definition.name}(${functionCall.arguments})`);
122
+ returnObject = JSON.stringify(yield f.call(JSON.parse(functionCall.arguments)));
123
+ logger.info(`Assistant called function: ${f.definition.name}(${functionCall.arguments}) => ${returnObject}`, 1000);
124
+ }
125
+ catch (error) {
126
+ logger.error(error.message);
127
+ }
128
+ if (!returnObject)
129
+ return;
130
+ return {
131
+ role: 'function',
132
+ name: f.definition.name,
133
+ content: returnObject,
134
+ };
135
+ });
136
+ }
137
+ static generateCode(messages, model, history, functions, messageModerators, includeSystemMessages = true, logLevel = 'info') {
138
+ return __awaiter(this, void 0, void 0, function* () {
139
+ const systemMessages = [
140
+ { role: 'system', content: 'Return only the code and exclude example usage, markdown, explanations, comments and notes.' },
141
+ { role: 'system', content: `Write code in typescript.` },
142
+ { role: 'system', content: `Declare explicit types for all function parameters.` },
143
+ { role: 'system', content: 'Export all functions and objects generated.' },
144
+ { role: 'system', content: 'Do not omit function implementations.' },
145
+ ];
146
+ const resolvedHistory = history ?
147
+ includeSystemMessages ?
148
+ history.push(systemMessages)
149
+ :
150
+ history
151
+ :
152
+ includeSystemMessages ?
153
+ new MessageHistory_1.MessageHistory().push(systemMessages)
154
+ :
155
+ undefined;
156
+ const code = yield this.generateResponse(messages, model, resolvedHistory, functions, messageModerators, logLevel);
157
+ return this.parseCodeFromMarkdown(code);
158
+ });
159
+ }
160
+ static updateCode(code, description, model, history, functions, messageModerators, includeSystemMessages = true, logLevel = 'info') {
161
+ return __awaiter(this, void 0, void 0, function* () {
162
+ return yield this.generateCode([this.updateCodeDescription(code, description)], model, history, functions, messageModerators, includeSystemMessages, logLevel);
163
+ });
164
+ }
165
+ static updateCodeDescription(code, description) {
166
+ return `Update this code:\n\n${code}\n\n${description}`;
167
+ }
168
+ static parseCodeFromMarkdown(code) {
169
+ if (!code.match(/```([\s\S]+?)```/g))
170
+ return code;
171
+ const filteredLines = [];
172
+ let inCodeBlock = false;
173
+ for (let line of code.split('\n')) {
174
+ if (line.startsWith('```')) {
175
+ inCodeBlock = !inCodeBlock;
176
+ if (!inCodeBlock)
177
+ filteredLines.push('');
178
+ continue;
179
+ }
180
+ if (inCodeBlock)
181
+ filteredLines.push(line);
182
+ }
183
+ // remove the last '' that will become a \n
184
+ // we only want spaces between code blocks
185
+ filteredLines.pop();
186
+ return filteredLines.join('\n');
187
+ }
188
+ static generateList(messages, model, history, functions, messageModerators, includeSystemMessages = true, logLevel = 'info') {
189
+ return __awaiter(this, void 0, void 0, function* () {
190
+ const systemMessages = [
191
+ { role: 'system', content: 'Return only the list and exclude example usage, markdown and all explanations, comments and notes.' },
192
+ { role: 'system', content: 'Separate each item in the list by a ;' },
193
+ ];
194
+ const resolvedHistory = history ?
195
+ includeSystemMessages ?
196
+ history.push(systemMessages)
197
+ :
198
+ history
199
+ :
200
+ includeSystemMessages ?
201
+ new MessageHistory_1.MessageHistory().push(systemMessages)
202
+ :
203
+ undefined;
204
+ const list = yield this.generateResponse(messages, model, resolvedHistory, functions, messageModerators, logLevel);
205
+ return list.split(';').map(item => item.trim());
206
+ });
207
+ }
208
+ }
209
+ exports.OpenAi = OpenAi;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Paragraph = void 0;
4
+ class Paragraph {
5
+ constructor() {
6
+ this.sentences = [];
7
+ }
8
+ add(sentence) {
9
+ this.sentences.push(sentence);
10
+ return this;
11
+ }
12
+ toString() {
13
+ if (this.sentences.length == 0)
14
+ return '';
15
+ return this.sentences.join(' ');
16
+ }
17
+ }
18
+ exports.Paragraph = Paragraph;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Sentence = void 0;
4
+ // Alina was here
5
+ class Sentence {
6
+ constructor() {
7
+ this.lines = [];
8
+ }
9
+ add(line) {
10
+ this.lines.push(line);
11
+ return this;
12
+ }
13
+ toString() {
14
+ if (this.lines.length == 0)
15
+ return '';
16
+ let sentence = this.lines.join(', ');
17
+ if (sentence.lastIndexOf('.') != (sentence.length - 1))
18
+ sentence += '.';
19
+ return sentence;
20
+ }
21
+ }
22
+ exports.Sentence = Sentence;