@empiricalrun/test-gen 0.55.0 → 0.56.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.
- package/CHANGELOG.md +27 -0
- package/dist/agent/browsing/run.js +4 -4
- package/dist/agent/browsing/utils.d.ts.map +1 -1
- package/dist/agent/browsing/utils.js +39 -21
- package/dist/agent/chat/agent-loop.d.ts +3 -1
- package/dist/agent/chat/agent-loop.d.ts.map +1 -1
- package/dist/agent/chat/agent-loop.js +7 -23
- package/dist/agent/chat/exports.d.ts +8 -0
- package/dist/agent/chat/exports.d.ts.map +1 -0
- package/dist/agent/chat/exports.js +7 -0
- package/dist/agent/chat/index.d.ts.map +1 -1
- package/dist/agent/chat/index.js +5 -0
- package/dist/agent/chat/repo.js +4 -4
- package/dist/agent/codegen/fix-ts-errors.js +3 -3
- package/dist/agent/codegen/generate-code-apply-changes.js +4 -4
- package/dist/agent/codegen/repo-edit.js +2 -2
- package/dist/agent/codegen/run.js +4 -4
- package/dist/agent/codegen/update-flow.js +4 -4
- package/dist/agent/codegen/utils.js +11 -11
- package/dist/bin/index.js +4 -2
- package/dist/bin/utils/context.js +12 -12
- package/dist/bin/utils/fs/index.js +4 -4
- package/dist/bin/utils/platform/web/index.js +19 -19
- package/dist/reporter/index.js +5 -5
- package/dist/test-build/index.d.ts +2 -9
- package/dist/test-build/index.d.ts.map +1 -1
- package/dist/test-build/index.js +18 -17
- package/dist/tool-call-service/index.d.ts +6 -9
- package/dist/tool-call-service/index.d.ts.map +1 -1
- package/dist/tool-call-service/index.js +50 -36
- package/dist/tools/download-build.d.ts +3 -0
- package/dist/tools/download-build.d.ts.map +1 -0
- package/dist/tools/download-build.js +39 -0
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +1 -1
- package/dist/tools/str_replace_editor.d.ts.map +1 -1
- package/dist/tools/str_replace_editor.js +20 -9
- package/dist/tools/utils/index.d.ts.map +1 -1
- package/package.json +7 -5
package/dist/bin/index.js
CHANGED
|
@@ -233,8 +233,10 @@ async function main() {
|
|
|
233
233
|
generationId: testGenConfig.options?.metadata.generationId,
|
|
234
234
|
projectRepoName: testGenConfig.options?.metadata.projectRepoName,
|
|
235
235
|
});
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
if (testGenConfig.build?.url) {
|
|
237
|
+
// Download the build if repo has a download script
|
|
238
|
+
await (0, test_build_1.downloadBuild)(testGenConfig.build.url);
|
|
239
|
+
}
|
|
238
240
|
if (completedOptions.useChat) {
|
|
239
241
|
await runChatAgent({
|
|
240
242
|
chatSessionId: completedOptions.chatSessionId,
|
|
@@ -4,14 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.fetchAppKnowledge = exports.generateTxtForRepository = exports.createRepoEditFilter = exports.contextForGeneration = exports.createGitIgnoreFileFilter = void 0;
|
|
7
|
-
const
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const ignore_1 = __importDefault(require("ignore"));
|
|
9
|
-
const
|
|
9
|
+
const fs_2 = require("./fs");
|
|
10
10
|
async function createGitIgnoreFileFilter() {
|
|
11
11
|
const ignoreFn = (0, ignore_1.default)();
|
|
12
|
-
if (
|
|
12
|
+
if (fs_1.default.existsSync(".gitignore")) {
|
|
13
13
|
// Not checking for nested gitignore
|
|
14
|
-
let gitignore =
|
|
14
|
+
let gitignore = fs_1.default.readFileSync(".gitignore").toString();
|
|
15
15
|
gitignore = `
|
|
16
16
|
${gitignore}
|
|
17
17
|
.git/
|
|
@@ -27,10 +27,10 @@ exports.createGitIgnoreFileFilter = createGitIgnoreFileFilter;
|
|
|
27
27
|
async function contextForGeneration(file) {
|
|
28
28
|
const filter = await createGitIgnoreFileFilter();
|
|
29
29
|
return {
|
|
30
|
-
codePrompt: await (0,
|
|
31
|
-
pomPrompt: await (0,
|
|
32
|
-
nonSpecFilePrompt: await (0,
|
|
33
|
-
testFileContent: file ?
|
|
30
|
+
codePrompt: await (0, fs_2.generatePromptFromDirectory)("./tests", filter),
|
|
31
|
+
pomPrompt: await (0, fs_2.generatePromptFromDirectory)("./pages", filter),
|
|
32
|
+
nonSpecFilePrompt: await (0, fs_2.generatePromptFromNonSpecFiles)("./tests", filter),
|
|
33
|
+
testFileContent: file ? fs_1.default.readFileSync(file, "utf-8") : "",
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
exports.contextForGeneration = contextForGeneration;
|
|
@@ -39,14 +39,14 @@ async function createRepoEditFilter() {
|
|
|
39
39
|
const gitIgnoreFilter = await createGitIgnoreFileFilter();
|
|
40
40
|
return (filePath) => {
|
|
41
41
|
return (gitIgnoreFilter(filePath) &&
|
|
42
|
-
(
|
|
42
|
+
(fs_1.default.statSync(filePath).isDirectory() || allowedExtensions.test(filePath)));
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
exports.createRepoEditFilter = createRepoEditFilter;
|
|
46
46
|
async function generateTxtForRepository() {
|
|
47
47
|
const filter = await createRepoEditFilter();
|
|
48
48
|
return {
|
|
49
|
-
prompt: await (0,
|
|
49
|
+
prompt: await (0, fs_2.generatePromptFromDirectory)(".", filter),
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
exports.generateTxtForRepository = generateTxtForRepository;
|
|
@@ -54,7 +54,7 @@ async function fetchAppKnowledge() {
|
|
|
54
54
|
let fileExists = true;
|
|
55
55
|
const appKnowledgePath = "./app_knowledge.md";
|
|
56
56
|
try {
|
|
57
|
-
|
|
57
|
+
fs_1.default.accessSync(appKnowledgePath);
|
|
58
58
|
}
|
|
59
59
|
catch (e) {
|
|
60
60
|
fileExists = false;
|
|
@@ -62,6 +62,6 @@ async function fetchAppKnowledge() {
|
|
|
62
62
|
if (!fileExists) {
|
|
63
63
|
return "";
|
|
64
64
|
}
|
|
65
|
-
return
|
|
65
|
+
return fs_1.default.readFileSync(appKnowledgePath, "utf-8");
|
|
66
66
|
}
|
|
67
67
|
exports.fetchAppKnowledge = fetchAppKnowledge;
|
|
@@ -4,19 +4,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.generatePromptFromNonSpecFiles = exports.generatePromptFromDirectory = exports.convertFileContentsToString = exports.readFilesInDirectory = void 0;
|
|
7
|
-
const
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
async function readFilesInDirectory(dir = "", filterFunc) {
|
|
10
10
|
let files = [];
|
|
11
|
-
const items =
|
|
11
|
+
const items = fs_1.default.readdirSync(dir);
|
|
12
12
|
const filteredItems = items.map((i) => path_1.default.join(dir, i)).filter(filterFunc);
|
|
13
13
|
for (const item of filteredItems) {
|
|
14
|
-
const stat =
|
|
14
|
+
const stat = fs_1.default.statSync(item);
|
|
15
15
|
if (stat.isDirectory()) {
|
|
16
16
|
files = files.concat(await readFilesInDirectory(item, filterFunc));
|
|
17
17
|
}
|
|
18
18
|
else if (stat.isFile()) {
|
|
19
|
-
const content =
|
|
19
|
+
const content = fs_1.default.readFileSync(item, "utf-8");
|
|
20
20
|
files.push({ filePath: item, content });
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.isSyntaxValid = exports.getVariableDeclarationsFromCode = exports.buildTestNamePrompt = exports.isTestPresent = exports.appendScopeToCreateTest = exports.addUserContextFixture = exports.importAllExportsStmtFromFilePaths = exports.injectCodeSnippetBySuiteChain = exports.replaceCreateTestWithNewCode = exports.getPageVariableNameFromCreateTest = exports.getFixtureImportPath = exports.removeTestOnly = exports.addNewImport = exports.formatCode = exports.lintErrors = exports.stripAndPrependImports = exports.validateTypescript = exports.appendToTestBlock = exports.findFirstSerialDescribeBlock = exports.hasTopLevelDescribeConfigureWithSerialMode = exports.hasTestBlock = exports.getTypescriptTestBlock = exports.getTestModuleAliasFromSourceFile = void 0;
|
|
7
7
|
const parser_1 = require("@babel/parser");
|
|
8
8
|
const eslint_1 = require("eslint");
|
|
9
|
-
const
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
11
|
const prettier_1 = __importDefault(require("prettier"));
|
|
12
12
|
const ts_morph_1 = require("ts-morph");
|
|
@@ -73,12 +73,12 @@ function getTypescriptTestBlock({ scenarioName, suites, content, }) {
|
|
|
73
73
|
}
|
|
74
74
|
exports.getTypescriptTestBlock = getTypescriptTestBlock;
|
|
75
75
|
function hasTestBlock({ testName, testSuites, filePath, }) {
|
|
76
|
-
if (!
|
|
76
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
77
77
|
return false;
|
|
78
78
|
}
|
|
79
79
|
const { testBlock } = getTypescriptTestBlock({
|
|
80
80
|
scenarioName: testName,
|
|
81
|
-
content:
|
|
81
|
+
content: fs_1.default.readFileSync(filePath, "utf-8"),
|
|
82
82
|
suites: testSuites,
|
|
83
83
|
});
|
|
84
84
|
return Boolean(testBlock);
|
|
@@ -107,7 +107,7 @@ function getParentDescribeNames(node) {
|
|
|
107
107
|
}
|
|
108
108
|
async function hasTopLevelDescribeConfigureWithSerialMode(filePath) {
|
|
109
109
|
const project = new ts_morph_1.Project();
|
|
110
|
-
const content =
|
|
110
|
+
const content = fs_1.default.readFileSync(filePath, "utf-8");
|
|
111
111
|
const sourceFile = project.createSourceFile("test.ts", content);
|
|
112
112
|
const statements = sourceFile.getStatements();
|
|
113
113
|
for (const statement of statements) {
|
|
@@ -188,7 +188,7 @@ exports.appendToTestBlock = appendToTestBlock;
|
|
|
188
188
|
function validateTypescript(filePath) {
|
|
189
189
|
// Create a compiler host to read files
|
|
190
190
|
const compilerHost = typescript_1.default.createCompilerHost({});
|
|
191
|
-
compilerHost.readFile = (file) =>
|
|
191
|
+
compilerHost.readFile = (file) => fs_1.default.readFileSync(file, "utf8");
|
|
192
192
|
// Create a program with a single source file
|
|
193
193
|
const program = typescript_1.default.createProgram([filePath], {
|
|
194
194
|
esModuleInterop: true,
|
|
@@ -245,12 +245,12 @@ async function lintErrors(filePath) {
|
|
|
245
245
|
});
|
|
246
246
|
const [result] = await eslint.lintFiles(filePath);
|
|
247
247
|
if (result?.output) {
|
|
248
|
-
|
|
248
|
+
fs_1.default.writeFileSync(filePath, result.output);
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
exports.lintErrors = lintErrors;
|
|
252
252
|
async function formatCode(filePath, trace) {
|
|
253
|
-
const fileContent =
|
|
253
|
+
const fileContent = fs_1.default.readFileSync(filePath, "utf8");
|
|
254
254
|
if (!fileContent) {
|
|
255
255
|
trace?.span({
|
|
256
256
|
name: "prettier-format-output",
|
|
@@ -264,7 +264,7 @@ async function formatCode(filePath, trace) {
|
|
|
264
264
|
filepath: filePath,
|
|
265
265
|
});
|
|
266
266
|
trace?.span({ name: "prettier-format-output", output: formattedContent });
|
|
267
|
-
|
|
267
|
+
fs_1.default.writeFileSync(filePath, formattedContent);
|
|
268
268
|
}
|
|
269
269
|
exports.formatCode = formatCode;
|
|
270
270
|
function addNewImport(contents, modules, pkg) {
|
|
@@ -272,11 +272,11 @@ function addNewImport(contents, modules, pkg) {
|
|
|
272
272
|
}
|
|
273
273
|
exports.addNewImport = addNewImport;
|
|
274
274
|
async function removeTestOnly(filePath) {
|
|
275
|
-
const contents =
|
|
275
|
+
const contents = fs_1.default.readFileSync(filePath, "utf8");
|
|
276
276
|
const updatedContent = contents
|
|
277
277
|
.replace("test.only(", "test(")
|
|
278
278
|
.replace("test.describe.only(", "test.describe(");
|
|
279
|
-
|
|
279
|
+
fs_1.default.writeFileSync(filePath, updatedContent);
|
|
280
280
|
}
|
|
281
281
|
exports.removeTestOnly = removeTestOnly;
|
|
282
282
|
function getFixtureImportPath(filePath) {
|
|
@@ -296,7 +296,7 @@ function getFixtureImportPath(filePath) {
|
|
|
296
296
|
}
|
|
297
297
|
exports.getFixtureImportPath = getFixtureImportPath;
|
|
298
298
|
async function getPageVariableNameFromCreateTest(filePath) {
|
|
299
|
-
const contents =
|
|
299
|
+
const contents = fs_1.default.readFileSync(filePath, "utf-8");
|
|
300
300
|
const project = new ts_morph_1.Project();
|
|
301
301
|
const sourceFile = project.createSourceFile("test.ts", contents);
|
|
302
302
|
const createTestNode = sourceFile.getFirstDescendant((node) => !!(node.isKind(ts_morph_1.SyntaxKind.CallExpression) &&
|
|
@@ -418,7 +418,7 @@ const importAllExportsStmtFromFilePaths = async (repoDir, filePaths, testFilePat
|
|
|
418
418
|
if (!importPath.startsWith(".")) {
|
|
419
419
|
importPath = "./" + importPath;
|
|
420
420
|
}
|
|
421
|
-
const file =
|
|
421
|
+
const file = fs_1.default.readFileSync(fullPath, "utf-8");
|
|
422
422
|
const project = new ts_morph_1.Project();
|
|
423
423
|
const sourceFile = project.createSourceFile("index.ts", file);
|
|
424
424
|
const exportedFunctions = sourceFile
|
|
@@ -440,7 +440,7 @@ async function addUserContextFixture({ scenarioName, filePath, suites, }) {
|
|
|
440
440
|
// TODO: remove this check when we support user context across repos
|
|
441
441
|
let fixtureContent = "";
|
|
442
442
|
try {
|
|
443
|
-
fixtureContent =
|
|
443
|
+
fixtureContent = fs_1.default.readFileSync("./tests/fixtures.ts", "utf-8");
|
|
444
444
|
}
|
|
445
445
|
catch (e) {
|
|
446
446
|
// do nothing
|
|
@@ -448,7 +448,7 @@ async function addUserContextFixture({ scenarioName, filePath, suites, }) {
|
|
|
448
448
|
if (!fixtureContent || !fixtureContent.includes("userContext")) {
|
|
449
449
|
return;
|
|
450
450
|
}
|
|
451
|
-
const fileContent =
|
|
451
|
+
const fileContent = fs_1.default.readFileSync(filePath, "utf-8");
|
|
452
452
|
const { testNode } = getTypescriptTestBlock({
|
|
453
453
|
scenarioName,
|
|
454
454
|
content: fileContent,
|
|
@@ -469,14 +469,14 @@ async function addUserContextFixture({ scenarioName, filePath, suites, }) {
|
|
|
469
469
|
.replace(",}", "}") // Remove any trailing commas
|
|
470
470
|
.replace("}", ", userContext }");
|
|
471
471
|
destructuringParam.replaceWithText(updatedImport);
|
|
472
|
-
|
|
472
|
+
fs_1.default.writeFileSync(filePath, fileContent.replace(currentBlock, testNode.getText()), "utf-8");
|
|
473
473
|
}
|
|
474
474
|
}
|
|
475
475
|
}
|
|
476
476
|
}
|
|
477
477
|
exports.addUserContextFixture = addUserContextFixture;
|
|
478
478
|
async function appendScopeToCreateTest(filePath, scopeVariables) {
|
|
479
|
-
const contents =
|
|
479
|
+
const contents = fs_1.default.readFileSync(filePath, "utf-8");
|
|
480
480
|
const project = new ts_morph_1.Project();
|
|
481
481
|
const sourceFile = project.createSourceFile("test.ts", contents);
|
|
482
482
|
const createTestNode = sourceFile
|
|
@@ -491,17 +491,17 @@ async function appendScopeToCreateTest(filePath, scopeVariables) {
|
|
|
491
491
|
const args = createTestNode.getArguments();
|
|
492
492
|
const scopeArgStr = `{ ${scopeVariables.join(", ")} }`;
|
|
493
493
|
createTestNode.insertArgument(args.length, scopeArgStr);
|
|
494
|
-
|
|
494
|
+
fs_1.default.writeFileSync(filePath, sourceFile.getFullText());
|
|
495
495
|
}
|
|
496
496
|
exports.appendScopeToCreateTest = appendScopeToCreateTest;
|
|
497
497
|
function isTestPresent({ specPath, testCase, }) {
|
|
498
498
|
let isTestPresent = true;
|
|
499
|
-
isTestPresent =
|
|
499
|
+
isTestPresent = fs_1.default.existsSync(specPath);
|
|
500
500
|
if (isTestPresent) {
|
|
501
501
|
const { testBlock } = getTypescriptTestBlock({
|
|
502
502
|
suites: testCase.suites,
|
|
503
503
|
scenarioName: testCase.name,
|
|
504
|
-
content:
|
|
504
|
+
content: fs_1.default.readFileSync(specPath, "utf-8"),
|
|
505
505
|
});
|
|
506
506
|
isTestPresent = Boolean(testBlock);
|
|
507
507
|
}
|
package/dist/reporter/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.TestGenUpdatesReporter = exports.setReporterConfig = exports.getReporter = void 0;
|
|
7
7
|
const r2_uploader_1 = require("@empiricalrun/r2-uploader");
|
|
8
|
-
const
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const logger_1 = require("../bin/logger");
|
|
11
11
|
const uploader_1 = require("../uploader");
|
|
@@ -93,10 +93,10 @@ class TestGenUpdatesReporter {
|
|
|
93
93
|
}
|
|
94
94
|
try {
|
|
95
95
|
// upload current screenshot to r2 and report it to reporter
|
|
96
|
-
if (!
|
|
97
|
-
|
|
96
|
+
if (!fs_1.default.existsSync(path_1.default.join(this.repoDir, "gen-assets"))) {
|
|
97
|
+
fs_1.default.mkdirSync(path_1.default.join(this.repoDir, "gen-assets"));
|
|
98
98
|
}
|
|
99
|
-
|
|
99
|
+
fs_1.default.writeFileSync(path_1.default.join(this.repoDir, "gen-assets", `current-view-${Date.now()}.png`), buffer);
|
|
100
100
|
const uploadDir = (0, uploader_1.getUploadPathForRun)(reporterConfig?.projectRepoName);
|
|
101
101
|
const files = await (0, r2_uploader_1.uploadDirectory)({
|
|
102
102
|
sourceDir: path_1.default.join(this.repoDir, "gen-assets"),
|
|
@@ -110,7 +110,7 @@ class TestGenUpdatesReporter {
|
|
|
110
110
|
type: "current-snapshot",
|
|
111
111
|
message: JSON.stringify({ type: "current-view", url }),
|
|
112
112
|
}));
|
|
113
|
-
|
|
113
|
+
fs_1.default.rmdirSync(path_1.default.join(this.repoDir, "gen-assets"), {
|
|
114
114
|
recursive: true,
|
|
115
115
|
});
|
|
116
116
|
}
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* method to download the build from the URL provided in the build object
|
|
4
|
-
* this is only used in cases like chrome extension as of yet.
|
|
5
|
-
* @export
|
|
6
|
-
* @param {Build} build
|
|
7
|
-
* @return {*} {Promise<void>}
|
|
8
|
-
*/
|
|
9
|
-
export declare function downloadBuild(build: Build): Promise<void>;
|
|
1
|
+
export declare function hasDownloadScript(): Promise<boolean>;
|
|
2
|
+
export declare function downloadBuild(buildUrl: string): Promise<void>;
|
|
10
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-build/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-build/index.ts"],"names":[],"mappings":"AAeA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAG1D;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUnE"}
|
package/dist/test-build/index.js
CHANGED
|
@@ -3,26 +3,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.downloadBuild = void 0;
|
|
7
|
-
const
|
|
6
|
+
exports.downloadBuild = exports.hasDownloadScript = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const logger_1 = require("../bin/logger");
|
|
9
9
|
const exec_1 = require("../utils/exec");
|
|
10
|
-
|
|
11
|
-
* method to download the build from the URL provided in the build object
|
|
12
|
-
* this is only used in cases like chrome extension as of yet.
|
|
13
|
-
* @export
|
|
14
|
-
* @param {Build} build
|
|
15
|
-
* @return {*} {Promise<void>}
|
|
16
|
-
*/
|
|
17
|
-
async function downloadBuild(build) {
|
|
18
|
-
const logger = new logger_1.CustomLogger({ useReporter: false });
|
|
10
|
+
async function getPackageJSON() {
|
|
19
11
|
const packageJSONPath = "package.json";
|
|
20
|
-
const packageJsonStr =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
const packageJsonStr = fs_1.default.readFileSync(packageJSONPath, "utf-8");
|
|
13
|
+
return JSON.parse(packageJsonStr);
|
|
14
|
+
}
|
|
15
|
+
async function hasDownloadScript() {
|
|
16
|
+
const packageJSON = await getPackageJSON();
|
|
17
|
+
return !!packageJSON.scripts["download"];
|
|
18
|
+
}
|
|
19
|
+
exports.hasDownloadScript = hasDownloadScript;
|
|
20
|
+
async function downloadBuild(buildUrl) {
|
|
21
|
+
const logger = new logger_1.CustomLogger({ useReporter: false });
|
|
22
|
+
const packageJSON = await getPackageJSON();
|
|
23
|
+
const buildDownloadScript = packageJSON.scripts["download"];
|
|
24
|
+
if (buildDownloadScript && buildUrl) {
|
|
25
|
+
logger.log(`Downloading build from ${buildUrl}`);
|
|
26
|
+
await (0, exec_1.cmd)(`npm run download ${buildUrl}`.split(" "), {
|
|
26
27
|
env: { ...Object(process.env) },
|
|
27
28
|
});
|
|
28
29
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Tool, ToolResult } from "@empiricalrun/llm/chat";
|
|
1
|
+
import { PendingToolCall, Tool, ToolResult } from "@empiricalrun/llm/chat";
|
|
2
2
|
import { SupportedChatModels } from "../agent/chat/types";
|
|
3
3
|
export type { SupportedChatModels };
|
|
4
4
|
type ToolExecutors = {
|
|
@@ -7,15 +7,12 @@ type ToolExecutors = {
|
|
|
7
7
|
export declare class ToolCallService {
|
|
8
8
|
tools: Tool[];
|
|
9
9
|
toolExecutors: ToolExecutors;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
chatSessionId: number | null;
|
|
11
|
+
selectedModel: SupportedChatModels;
|
|
12
|
+
constructor(chatSessionId: number | null, selectedModel: SupportedChatModels);
|
|
13
|
+
getTools(): Promise<{
|
|
12
14
|
tools: Tool[];
|
|
13
15
|
}>;
|
|
14
|
-
execute(
|
|
15
|
-
tool: {
|
|
16
|
-
name: string;
|
|
17
|
-
input: any;
|
|
18
|
-
};
|
|
19
|
-
}, isRemote: boolean): Promise<ToolResult>;
|
|
16
|
+
execute(toolCalls: PendingToolCall[], isRemote: boolean): Promise<ToolResult[]>;
|
|
20
17
|
}
|
|
21
18
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tool-call-service/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tool-call-service/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAa1D,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,KAAK,aAAa,GAAG;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CACpD,CAAC;AAyBF,qBAAa,eAAe;IAC1B,KAAK,EAAE,IAAI,EAAE,CAAM;IACnB,aAAa,EAAE,aAAa,CAAM;IAClC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,mBAAmB,CAAC;gBAGjC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,aAAa,EAAE,mBAAmB;IAgB9B,QAAQ;;;IAaR,OAAO,CACX,SAAS,EAAE,eAAe,EAAE,EAC5B,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,UAAU,EAAE,CAAC;CAqCzB"}
|
|
@@ -4,13 +4,14 @@ exports.ToolCallService = void 0;
|
|
|
4
4
|
const client_sqs_1 = require("@aws-sdk/client-sqs");
|
|
5
5
|
const commit_and_create_pr_1 = require("../tools/commit-and-create-pr");
|
|
6
6
|
const diagnosis_fetcher_1 = require("../tools/diagnosis-fetcher");
|
|
7
|
+
const download_build_1 = require("../tools/download-build");
|
|
7
8
|
const environment_crud_1 = require("../tools/environment-crud");
|
|
8
9
|
const grep_1 = require("../tools/grep");
|
|
9
10
|
const str_replace_editor_1 = require("../tools/str_replace_editor");
|
|
10
11
|
const test_gen_browser_1 = require("../tools/test-gen-browser");
|
|
11
12
|
const test_run_1 = require("../tools/test-run");
|
|
12
13
|
const test_run_fetcher_1 = require("../tools/test-run-fetcher");
|
|
13
|
-
async function sendToolRequestToRemoteQueue(
|
|
14
|
+
async function sendToolRequestToRemoteQueue(payload) {
|
|
14
15
|
const sqs = new client_sqs_1.SQSClient({
|
|
15
16
|
region: process.env.AWS_REGION,
|
|
16
17
|
credentials: {
|
|
@@ -18,18 +19,22 @@ async function sendToolRequestToRemoteQueue(toolName, input) {
|
|
|
18
19
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
19
20
|
},
|
|
20
21
|
});
|
|
21
|
-
const queueUrl =
|
|
22
|
+
const queueUrl = process.env.TOOL_REQUEST_QUEUE_URL;
|
|
22
23
|
await sqs.send(new client_sqs_1.SendMessageCommand({
|
|
23
24
|
QueueUrl: queueUrl,
|
|
24
|
-
MessageBody: JSON.stringify(
|
|
25
|
-
MessageGroupId:
|
|
26
|
-
MessageDeduplicationId:
|
|
25
|
+
MessageBody: JSON.stringify(payload),
|
|
26
|
+
MessageGroupId: payload.requestId,
|
|
27
|
+
MessageDeduplicationId: payload.requestId, // unique id for the tool request
|
|
27
28
|
}));
|
|
28
29
|
}
|
|
29
30
|
class ToolCallService {
|
|
30
31
|
tools = [];
|
|
31
32
|
toolExecutors = {};
|
|
32
|
-
|
|
33
|
+
chatSessionId;
|
|
34
|
+
selectedModel;
|
|
35
|
+
constructor(chatSessionId, selectedModel) {
|
|
36
|
+
this.chatSessionId = chatSessionId;
|
|
37
|
+
this.selectedModel = selectedModel;
|
|
33
38
|
this.tools = [
|
|
34
39
|
grep_1.grepTool,
|
|
35
40
|
test_run_1.runTestTool,
|
|
@@ -38,49 +43,58 @@ class ToolCallService {
|
|
|
38
43
|
test_gen_browser_1.generateTestWithBrowserAgent,
|
|
39
44
|
commit_and_create_pr_1.commitAndPushChangesTool,
|
|
40
45
|
environment_crud_1.getEnvironmentTool,
|
|
46
|
+
download_build_1.downloadBuildTool,
|
|
41
47
|
];
|
|
42
48
|
}
|
|
43
|
-
async getTools(
|
|
44
|
-
if (!selectedModel.startsWith("claude")) {
|
|
49
|
+
async getTools() {
|
|
50
|
+
if (!this.selectedModel.startsWith("claude")) {
|
|
45
51
|
this.tools.push(...str_replace_editor_1.textEditorTools);
|
|
46
52
|
}
|
|
47
53
|
this.tools.forEach((tool) => {
|
|
48
54
|
this.toolExecutors[tool.schema.name] = tool.execute;
|
|
49
55
|
});
|
|
50
|
-
if (selectedModel.startsWith("claude")) {
|
|
56
|
+
if (this.selectedModel.startsWith("claude")) {
|
|
51
57
|
this.toolExecutors["str_replace_editor"] = str_replace_editor_1.strReplaceEditorExecutor;
|
|
52
58
|
}
|
|
53
59
|
return { tools: this.tools };
|
|
54
60
|
}
|
|
55
|
-
async execute(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
async execute(toolCalls, isRemote) {
|
|
62
|
+
if (isRemote && this.chatSessionId) {
|
|
63
|
+
console.log("Executing tool remotely", toolCalls);
|
|
64
|
+
await sendToolRequestToRemoteQueue({
|
|
65
|
+
toolCalls,
|
|
66
|
+
requestId: crypto.randomUUID(),
|
|
67
|
+
chatSessionId: this.chatSessionId,
|
|
68
|
+
selectedModel: this.selectedModel,
|
|
69
|
+
});
|
|
70
|
+
return toolCalls.map(() => ({
|
|
71
|
+
isError: false,
|
|
72
|
+
result: `Tool request sent to remote queue to execute.`,
|
|
73
|
+
}));
|
|
63
74
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
else {
|
|
76
|
+
const toolResults = [];
|
|
77
|
+
for (const toolCall of toolCalls) {
|
|
78
|
+
const toolExecutor = this.toolExecutors[toolCall.name];
|
|
79
|
+
if (!toolExecutor) {
|
|
80
|
+
toolResults.push({
|
|
81
|
+
isError: true,
|
|
82
|
+
result: `Invalid function/tool call: invalid_tool_call not found`,
|
|
83
|
+
});
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const result = await toolExecutor(toolCall.input);
|
|
88
|
+
toolResults.push(result);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
toolResults.push({
|
|
92
|
+
isError: true,
|
|
93
|
+
result: error instanceof Error ? error.message : String(error),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
74
96
|
}
|
|
75
|
-
|
|
76
|
-
return await toolExecutor(tool.input);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
return {
|
|
81
|
-
isError: true,
|
|
82
|
-
result: error instanceof Error ? error.message : String(error),
|
|
83
|
-
};
|
|
97
|
+
return toolResults;
|
|
84
98
|
}
|
|
85
99
|
}
|
|
86
100
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download-build.d.ts","sourceRoot":"","sources":["../../src/tools/download-build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAKnD,eAAO,MAAM,iBAAiB,EAAE,IAkC/B,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.downloadBuildTool = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const test_build_1 = require("../test-build");
|
|
6
|
+
exports.downloadBuildTool = {
|
|
7
|
+
schema: {
|
|
8
|
+
name: "downloadBuild",
|
|
9
|
+
description: `Download a build from a build URL. If you do not have
|
|
10
|
+
have a build URL, you can try getting the environment details with the getEnvironment tool.
|
|
11
|
+
Environment details will include the build URL.`,
|
|
12
|
+
parameters: zod_1.z.object({
|
|
13
|
+
buildUrl: zod_1.z.string().describe("The URL of the build to download"),
|
|
14
|
+
}),
|
|
15
|
+
},
|
|
16
|
+
execute: async (input) => {
|
|
17
|
+
if (!(await (0, test_build_1.hasDownloadScript)())) {
|
|
18
|
+
return {
|
|
19
|
+
isError: true,
|
|
20
|
+
result: `This repo does not have a download script in package.json.
|
|
21
|
+
You probably don't need to worry about this, since it means this repo does not have a build to download.`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const { buildUrl } = input;
|
|
25
|
+
try {
|
|
26
|
+
await (0, test_build_1.downloadBuild)(buildUrl);
|
|
27
|
+
return {
|
|
28
|
+
isError: false,
|
|
29
|
+
result: "Build downloaded successfully",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
return {
|
|
34
|
+
isError: true,
|
|
35
|
+
result: `Failed to download build: ${error}`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
};
|
package/dist/tools/grep.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/tools/grep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/tools/grep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,wBAAwB,CAAC;AAmB/D,eAAO,MAAM,QAAQ,EAAE,IAgDtB,CAAC"}
|
package/dist/tools/grep.js
CHANGED
|
@@ -5,7 +5,6 @@ const child_process_1 = require("child_process");
|
|
|
5
5
|
const util_1 = require("util");
|
|
6
6
|
const zod_1 = require("zod");
|
|
7
7
|
const repo_tree_1 = require("../utils/repo-tree");
|
|
8
|
-
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
9
8
|
const GrepInputSchema = zod_1.z.object({
|
|
10
9
|
pattern: zod_1.z.string().describe("The pattern to search for"),
|
|
11
10
|
directory: zod_1.z
|
|
@@ -40,6 +39,7 @@ exports.grepTool = {
|
|
|
40
39
|
// Using -n to show line numbers and removed -l to show actual matches
|
|
41
40
|
cmd = `find ${dir} ${excludeFind} -name "${input.filePattern}" -exec grep -rin "${input.pattern}" {} \\;`;
|
|
42
41
|
}
|
|
42
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
43
43
|
const { stdout, stderr } = await execAsync(cmd);
|
|
44
44
|
if (stdout) {
|
|
45
45
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"str_replace_editor.d.ts","sourceRoot":"","sources":["../../src/tools/str_replace_editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AA2B1D,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAwC1D;AAMD;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"str_replace_editor.d.ts","sourceRoot":"","sources":["../../src/tools/str_replace_editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AA2B1D,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAwC1D;AAMD;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,UAAU,CAAC,CAmJrB;AA+FD,eAAO,MAAM,eAAe,EAAE,IAAI,EAKjC,CAAC"}
|