@jpoly1219/context-extractor 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
package/dist/app.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { Language, Context } from "./types";
2
+ export declare class App {
3
+ private language;
4
+ private languageDriver;
5
+ private languageServer;
6
+ private lspClient;
7
+ private sketchPath;
8
+ private repoPath;
9
+ private result;
10
+ private timeout;
11
+ constructor(language: Language, sketchPath: string, repoPath: string);
12
+ init(): Promise<void>;
13
+ run(): Promise<void>;
14
+ close(): void;
15
+ getSavedResult(): Context | null;
16
+ }
17
+ export declare class CompletionEngine {
18
+ private language;
19
+ private config;
20
+ private sketchPath;
21
+ constructor(language: Language, sketchPath: string, configPath: string);
22
+ completeWithLLM(context: Context): Promise<string>;
23
+ generateTypesAndHeadersPrompt(sketchFileContent: string, holeType: string, relevantTypes: string, relevantHeaders: string): {
24
+ role: string;
25
+ content: string;
26
+ }[];
27
+ }
package/dist/app.js ADDED
@@ -0,0 +1,285 @@
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 __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.CompletionEngine = exports.App = void 0;
30
+ const path = __importStar(require("path"));
31
+ const fs = __importStar(require("fs"));
32
+ const child_process_1 = require("child_process");
33
+ const child_process_2 = require("child_process");
34
+ const openai_1 = __importDefault(require("openai"));
35
+ // import { LspClient, JSONRPCEndpoint } from "../ts-lsp-client-dist/src/main";
36
+ const ts_lsp_client_1 = require("ts-lsp-client");
37
+ const types_1 = require("./types");
38
+ // TODO: Bundle the drivers as barrel exports.
39
+ const typescript_driver_1 = require("./typescript-driver");
40
+ const ocaml_driver_1 = require("./ocaml-driver");
41
+ const utils_1 = require("./utils");
42
+ class App {
43
+ constructor(language, sketchPath, repoPath) {
44
+ // private result: {
45
+ // hole: string;
46
+ // relevantTypes: string[];
47
+ // relevantHeaders: string[];
48
+ // } | null = null;
49
+ this.result = null;
50
+ // Optional timeout for forced termination
51
+ this.timeout = setTimeout(() => {
52
+ if (!this.languageServer.killed) {
53
+ console.log('Forcibly killing the process...');
54
+ this.languageServer.kill('SIGKILL');
55
+ }
56
+ }, 5000);
57
+ this.language = language;
58
+ this.sketchPath = sketchPath;
59
+ this.repoPath = repoPath;
60
+ const r = (() => {
61
+ switch (language) {
62
+ case types_1.Language.TypeScript: {
63
+ this.languageDriver = new typescript_driver_1.TypeScriptDriver();
64
+ return (0, child_process_1.spawn)("typescript-language-server", ["--stdio"], { stdio: ["pipe", "pipe", "pipe"] });
65
+ }
66
+ case types_1.Language.OCaml: {
67
+ this.languageDriver = new ocaml_driver_1.OcamlDriver();
68
+ try {
69
+ (0, child_process_2.execSync)(`eval $(opam env --switch=. --set-switch)`, { shell: "/bin/bash" });
70
+ // execSync("opam switch .", { shell: "/bin/bash" })
71
+ const currDir = __dirname;
72
+ process.chdir(path.dirname(sketchPath));
73
+ // execSync("which dune", { shell: "/bin/bash" })
74
+ (0, child_process_1.spawn)("dune", ["build", "-w"]);
75
+ process.chdir(currDir);
76
+ }
77
+ catch (err) {
78
+ console.log("ERROR:", err);
79
+ }
80
+ // TODO: Spawn a dune build -w on sketch directory.
81
+ // try {
82
+ // execSync("which dune", { shell: "/bin/bash" })
83
+ // spawn("dune", ["build", "-w"]);
84
+ // } catch (err) {
85
+ // console.log("ERROR:", err)
86
+ // }
87
+ // process.chdir(currDir);
88
+ return (0, child_process_1.spawn)("ocamllsp", ["--stdio"]);
89
+ }
90
+ }
91
+ })();
92
+ const e = new ts_lsp_client_1.JSONRPCEndpoint(r.stdin, r.stdout);
93
+ const c = new ts_lsp_client_1.LspClient(e);
94
+ this.languageServer = r;
95
+ this.lspClient = c;
96
+ this.languageServer.on('close', (code) => {
97
+ if (code !== 0) {
98
+ console.log(`ls process exited with code ${code}`);
99
+ }
100
+ });
101
+ // Clear timeout once the process exits
102
+ this.languageServer.on('exit', () => {
103
+ clearTimeout(this.timeout);
104
+ console.log('Process terminated cleanly.');
105
+ });
106
+ // const logFile = fs.createWriteStream("log.txt");
107
+ // r.stdout.on('data', (d) => logFile.write(d));
108
+ }
109
+ async init() {
110
+ await this.languageDriver.init(this.lspClient, this.sketchPath);
111
+ }
112
+ async run() {
113
+ // const outputFile = fs.createWriteStream("output.txt");
114
+ try {
115
+ await this.init();
116
+ const holeContext = await this.languageDriver.getHoleContext(this.lspClient, this.sketchPath);
117
+ const relevantTypes = await this.languageDriver.extractRelevantTypes(this.lspClient, holeContext.fullHoverResult, holeContext.functionName, holeContext.range.start.line, holeContext.range.end.line, new Map(), holeContext.source);
118
+ // Postprocess the map.
119
+ if (this.language === types_1.Language.TypeScript) {
120
+ relevantTypes.delete("_()");
121
+ for (const [k, { typeSpan: v, sourceFile: src }] of relevantTypes.entries()) {
122
+ relevantTypes.set(k, { typeSpan: v.slice(0, -1), sourceFile: src });
123
+ }
124
+ }
125
+ else if (this.language === types_1.Language.OCaml) {
126
+ relevantTypes.delete("_");
127
+ }
128
+ console.log(path.join(path.dirname(this.sketchPath), `sketch${path.extname(this.sketchPath)}`));
129
+ let repo = [];
130
+ if (this.language === types_1.Language.TypeScript) {
131
+ repo = (0, utils_1.getAllTSFiles)(this.repoPath);
132
+ }
133
+ else if (this.language === types_1.Language.OCaml) {
134
+ repo = (0, utils_1.getAllOCamlFiles)(this.repoPath);
135
+ }
136
+ const relevantHeaders = await this.languageDriver.extractRelevantHeaders(this.lspClient, repo, relevantTypes, holeContext.functionTypeSpan);
137
+ // Postprocess the map.
138
+ if (this.language === types_1.Language.TypeScript) {
139
+ relevantTypes.delete("");
140
+ for (const [k, { typeSpan: v, sourceFile: src }] of relevantTypes.entries()) {
141
+ relevantTypes.set(k, { typeSpan: v + ";", sourceFile: src });
142
+ }
143
+ for (const obj of relevantHeaders) {
144
+ obj.typeSpan += ";";
145
+ }
146
+ }
147
+ const relevantTypesToReturn = new Map();
148
+ relevantTypes.forEach(({ typeSpan: v, sourceFile: src }, _) => {
149
+ if (relevantTypesToReturn.has(src)) {
150
+ const updated = relevantTypesToReturn.get(src);
151
+ updated.push(v);
152
+ relevantTypesToReturn.set(src, updated);
153
+ }
154
+ else {
155
+ relevantTypesToReturn.set(src, [v]);
156
+ }
157
+ });
158
+ const relevantHeadersToReturn = new Map();
159
+ relevantHeaders.forEach(({ typeSpan: v, sourceFile: src }) => {
160
+ if (relevantHeadersToReturn.has(src)) {
161
+ const updated = relevantHeadersToReturn.get(src);
162
+ if (!updated.includes(v)) {
163
+ updated.push(v);
164
+ }
165
+ relevantHeadersToReturn.set(src, updated);
166
+ }
167
+ else {
168
+ relevantHeadersToReturn.set(src, [v]);
169
+ }
170
+ });
171
+ this.result = {
172
+ holeType: holeContext.functionTypeSpan,
173
+ relevantTypes: relevantTypesToReturn,
174
+ relevantHeaders: relevantHeadersToReturn
175
+ };
176
+ }
177
+ catch (err) {
178
+ console.error("Error during execution:", err);
179
+ throw err;
180
+ }
181
+ finally {
182
+ // outputFile.end();
183
+ }
184
+ }
185
+ close() {
186
+ // TODO:
187
+ try {
188
+ this.lspClient.exit();
189
+ }
190
+ catch (err) {
191
+ console.log(err);
192
+ }
193
+ }
194
+ getSavedResult() {
195
+ return this.result;
196
+ }
197
+ }
198
+ exports.App = App;
199
+ class CompletionEngine {
200
+ constructor(language, sketchPath, configPath) {
201
+ this.language = language;
202
+ this.config = JSON.parse(fs.readFileSync(configPath, "utf8"));
203
+ this.sketchPath = sketchPath;
204
+ }
205
+ async completeWithLLM(context) {
206
+ let joinedTypes = "";
207
+ let joinedHeaders = "";
208
+ context.relevantTypes.forEach((v, _) => {
209
+ joinedTypes = joinedTypes + v.join("\n") + "\n";
210
+ });
211
+ context.relevantHeaders.forEach((v, _) => {
212
+ joinedHeaders = joinedHeaders + v.join("\n") + "\n";
213
+ });
214
+ // Create a prompt.
215
+ const prompt = this.generateTypesAndHeadersPrompt(
216
+ // fs.readFileSync(path.join(targetDirectoryPath, "sketch.ts"), "utf8"),
217
+ fs.readFileSync(this.sketchPath, "utf8"), context.holeType, joinedTypes, joinedHeaders);
218
+ // Call the LLM to get completion results back.
219
+ const apiBase = this.config.apiBase;
220
+ const deployment = this.config.deployment;
221
+ const model = this.config.gptModel;
222
+ const apiVersion = this.config.apiVersion;
223
+ const apiKey = this.config.apiKey;
224
+ const openai = new openai_1.default({
225
+ apiKey,
226
+ baseURL: `${apiBase}/openai/deployments/${deployment}`,
227
+ defaultQuery: { "api-version": apiVersion },
228
+ defaultHeaders: { "api-key": apiKey }
229
+ });
230
+ const llmResult = await openai.chat.completions.create({
231
+ model,
232
+ messages: prompt,
233
+ temperature: this.config.temperature
234
+ });
235
+ return llmResult.choices[0].message.content;
236
+ }
237
+ generateTypesAndHeadersPrompt(sketchFileContent, holeType, relevantTypes, relevantHeaders) {
238
+ let holeConstruct = "";
239
+ switch (this.language) {
240
+ case types_1.Language.TypeScript: {
241
+ holeConstruct = "_()";
242
+ }
243
+ case types_1.Language.OCaml: {
244
+ holeConstruct = "_";
245
+ }
246
+ }
247
+ const prompt = [{
248
+ role: "system",
249
+ content: [
250
+ "CODE COMPLETION INSTRUCTIONS:",
251
+ `- Reply with a functional, idiomatic replacement for the program hole marked '${holeConstruct}' in the provided TypeScript program sketch`,
252
+ `- Reply only with a single replacement term for the unqiue distinguished hole marked '${holeConstruct}'`,
253
+ "Reply only with code",
254
+ "- DO NOT include the program sketch in your reply",
255
+ "- DO NOT include a period at the end of your response and DO NOT use markdown",
256
+ "- DO NOT include a type signature for the program hole, as this is redundant and is already in the provided program sketch"
257
+ ].join("\n"),
258
+ }];
259
+ let userPrompt = {
260
+ role: "user",
261
+ content: ""
262
+ };
263
+ if (relevantTypes) {
264
+ userPrompt.content +=
265
+ `# The expected type of the goal completion is ${holeType} #
266
+
267
+ # The following type definitions are likely relevant: #
268
+ ${relevantTypes}
269
+
270
+ `;
271
+ }
272
+ if (relevantHeaders) {
273
+ userPrompt.content += `
274
+ # Consider using these variables relevant to the expected type: #
275
+ ${relevantHeaders}
276
+
277
+ `;
278
+ }
279
+ userPrompt.content += `# Program Sketch to be completed: #\n${(0, utils_1.removeLines)(sketchFileContent).join("\n")}`;
280
+ prompt.push(userPrompt);
281
+ return prompt;
282
+ }
283
+ ;
284
+ }
285
+ exports.CompletionEngine = CompletionEngine;
@@ -0,0 +1,17 @@
1
+ import { relevantTypeObject, varsObject, typesObject } from "./types";
2
+ declare const createDatabaseWithCodeQL: (pathToCodeQL: string, targetPath: string) => string;
3
+ declare const extractHoleType: (pathToCodeQL: string, pathToQuery: string, pathToDatabase: string, outDir: string) => typesObject;
4
+ declare const extractRelevantTypesWithCodeQL: (pathToCodeQL: string, pathToQuery: string, pathToDatabase: string, outDir: string) => Map<string, relevantTypeObject>;
5
+ declare const extractHeadersWithCodeQL: (pathToCodeQL: string, pathToQuery: string, pathToDatabase: string, outDir: string) => Map<string, varsObject>;
6
+ declare const extractTypesAndLocations: (pathToCodeQL: string, pathToQuery: string, pathToDatabase: string, outDir: string) => {
7
+ locationToType: Map<string, string[]>;
8
+ typeToLocation: Map<string, string>;
9
+ };
10
+ declare const extractRelevantContextWithCodeQL: (pathToCodeQL: string, pathToQuery: string, pathToDatabase: string, outDir: string, headers: Map<string, varsObject>, relevantTypes: Map<string, relevantTypeObject>) => Set<string>;
11
+ declare const getRelevantHeaders: (pathToCodeQL: string, pathToQuery: string, pathToDatabase: string, outDir: string, headers: Map<string, varsObject>, holeType: typesObject) => Set<string>;
12
+ declare const getRelevantHeaders3: (pathToCodeQL: string, pathToQuery: string, pathToDatabase: string, outDir: string, headers: Map<string, varsObject>, holeType: typesObject, relevantTypes: Map<string, relevantTypeObject>) => Set<string>;
13
+ declare const getRelevantHeaders4: (pathToCodeQL: string, pathToQueryDir: string, pathToDatabase: string, outDir: string, headers: Map<string, varsObject>, holeType: typesObject, relevantTypes: Map<string, relevantTypeObject>, knownTypeLocations: {
14
+ locationToType: Map<string, string[]>;
15
+ typeToLocation: Map<string, string>;
16
+ }) => Set<string>;
17
+ export { createDatabaseWithCodeQL, extractHoleType, extractRelevantTypesWithCodeQL, extractHeadersWithCodeQL, extractRelevantContextWithCodeQL, extractTypesAndLocations, getRelevantHeaders, getRelevantHeaders3, getRelevantHeaders4 };