@empiricalrun/test-gen 0.42.13 → 0.42.14
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 +6 -0
- package/dist/agent/codegen/generate-code-apply-changes.d.ts +13 -0
- package/dist/agent/codegen/generate-code-apply-changes.d.ts.map +1 -0
- package/dist/agent/codegen/generate-code-apply-changes.js +421 -0
- package/dist/agent/codegen/repo-edit.d.ts +9 -16
- package/dist/agent/codegen/repo-edit.d.ts.map +1 -1
- package/dist/agent/codegen/repo-edit.js +73 -28
- package/dist/agent/codegen/run.d.ts.map +1 -1
- package/dist/agent/codegen/run.js +1 -0
- package/dist/agent/codegen/test-update-feedback.d.ts +1 -6
- package/dist/agent/codegen/test-update-feedback.d.ts.map +1 -1
- package/dist/agent/codegen/types.d.ts +20 -0
- package/dist/agent/codegen/types.d.ts.map +1 -0
- package/dist/agent/codegen/types.js +8 -0
- package/dist/agent/codegen/update-flow.d.ts +3 -8
- package/dist/agent/codegen/update-flow.d.ts.map +1 -1
- package/dist/agent/codegen/update-flow.js +2 -2
- package/dist/agent/codegen/utils.d.ts +38 -33
- package/dist/agent/codegen/utils.d.ts.map +1 -1
- package/dist/agent/codegen/utils.js +191 -110
- package/dist/bin/index.js +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { TraceClient } from "@empiricalrun/llm";
|
|
2
|
+
import { CustomLogger } from "../../bin/logger";
|
|
3
|
+
import { CodeUpdate } from "./types";
|
|
4
|
+
export declare function systemPromptBuilderForRepoEdit(files: string): string;
|
|
5
|
+
export declare function generateCodeAndApplyChanges({ task, trace, logger, getRelevantFiles, }: {
|
|
6
|
+
task: string;
|
|
7
|
+
trace?: TraceClient;
|
|
8
|
+
logger?: CustomLogger;
|
|
9
|
+
getRelevantFiles: () => Promise<{
|
|
10
|
+
prompt: string | undefined;
|
|
11
|
+
}>;
|
|
12
|
+
}): Promise<CodeUpdate[]>;
|
|
13
|
+
//# sourceMappingURL=generate-code-apply-changes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-code-apply-changes.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/generate-code-apply-changes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AASrE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAOhD,OAAO,EAAsB,UAAU,EAAsB,MAAM,SAAS,CAAC;AAqL7E,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,MAAM,UAkD3D;AAgED,wBAAsB,2BAA2B,CAAC,EAChD,IAAI,EACJ,KAAK,EACL,MAAM,EACN,gBAAgB,GACjB,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,gBAAgB,EAAE,MAAM,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC,CAAC;CACjE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAyLxB"}
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateCodeAndApplyChanges = exports.systemPromptBuilderForRepoEdit = void 0;
|
|
7
|
+
const llm_1 = require("@empiricalrun/llm");
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const path_1 = require("path");
|
|
10
|
+
const constants_1 = require("../../constants");
|
|
11
|
+
const utils_1 = require("../utils");
|
|
12
|
+
const types_1 = require("./types");
|
|
13
|
+
const utils_2 = require("./utils");
|
|
14
|
+
function getCodeEditorToolCalls() {
|
|
15
|
+
const strReplace = {
|
|
16
|
+
name: "code-block-replace",
|
|
17
|
+
description: "replace the code block with the provided code",
|
|
18
|
+
schema: {
|
|
19
|
+
type: "function",
|
|
20
|
+
function: {
|
|
21
|
+
name: types_1.CodeEditorToolCall.STR_REPLACE,
|
|
22
|
+
description: "replace the code block with the provided code",
|
|
23
|
+
parameters: {
|
|
24
|
+
type: "object",
|
|
25
|
+
properties: {
|
|
26
|
+
reason: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "reason for why the current block is selected for replacement and why is it needed.",
|
|
29
|
+
},
|
|
30
|
+
filePath: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "path of the file where the code block is present",
|
|
33
|
+
},
|
|
34
|
+
oldCode: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "old code block to be replaced",
|
|
37
|
+
},
|
|
38
|
+
newCode: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "new code block to replace the old code block",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
required: ["reason", "filePath", "oldCode", "newCode"],
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
const createFile = {
|
|
50
|
+
name: "create_file",
|
|
51
|
+
description: "create a new file with the provided code if the file doesn't exist",
|
|
52
|
+
schema: {
|
|
53
|
+
type: "function",
|
|
54
|
+
function: {
|
|
55
|
+
name: types_1.CodeEditorToolCall.CREATE_FILE,
|
|
56
|
+
description: "create a new file with the provided code if the file doesn't exist",
|
|
57
|
+
parameters: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: {
|
|
60
|
+
reason: {
|
|
61
|
+
type: "string",
|
|
62
|
+
description: "reason for why the file is needed and what is the purpose of the file.",
|
|
63
|
+
},
|
|
64
|
+
filePath: {
|
|
65
|
+
type: "string",
|
|
66
|
+
description: "path of the file that needs to be created",
|
|
67
|
+
},
|
|
68
|
+
code: {
|
|
69
|
+
type: "string",
|
|
70
|
+
description: "code to be written to the file",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
required: ["reason", "filePath", "code"],
|
|
74
|
+
additionalProperties: false,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
return [strReplace, createFile].map((tool) => tool.schema);
|
|
80
|
+
}
|
|
81
|
+
function getPlannerToolCalls() {
|
|
82
|
+
const planner = {
|
|
83
|
+
name: "change_plan",
|
|
84
|
+
description: "Provides the plan for the changes to be made",
|
|
85
|
+
schema: {
|
|
86
|
+
type: "function",
|
|
87
|
+
function: {
|
|
88
|
+
name: "change_plan",
|
|
89
|
+
description: "Provides the plan for the changes to be made",
|
|
90
|
+
parameters: {
|
|
91
|
+
type: "object",
|
|
92
|
+
properties: {
|
|
93
|
+
reason: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description: "Reason for providing the plan. Why a certain file needed the change ? Are you sure that all the changes required are present in the plan ?",
|
|
96
|
+
},
|
|
97
|
+
plan: {
|
|
98
|
+
type: "string",
|
|
99
|
+
description: "Plan given to code agent to execute. The plan should be readable and easy to understand.",
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
additionalProperties: false,
|
|
103
|
+
required: ["reason", "plan"],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
const exit = {
|
|
109
|
+
name: "exit",
|
|
110
|
+
description: "Called when there are no more changes to be made. All the changes are done.",
|
|
111
|
+
schema: {
|
|
112
|
+
type: "function",
|
|
113
|
+
function: {
|
|
114
|
+
name: "exit",
|
|
115
|
+
description: "Called when there are no more changes to be made. All the changes are done",
|
|
116
|
+
parameters: {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {
|
|
119
|
+
reason: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "Reason for marking the task as done.",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
additionalProperties: false,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
return [planner, exit].map((tool) => tool.schema);
|
|
130
|
+
}
|
|
131
|
+
async function getPlanForCodeEditorAgent({ prompt, trace, }) {
|
|
132
|
+
const promptSpan = trace?.span({
|
|
133
|
+
name: "planner-agent",
|
|
134
|
+
input: {
|
|
135
|
+
prompt,
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
const llm = new llm_1.LLM({
|
|
139
|
+
trace: promptSpan,
|
|
140
|
+
provider: "openai",
|
|
141
|
+
defaultModel: "o3-mini-2025-01-31",
|
|
142
|
+
providerApiKey: constants_1.MODEL_API_KEYS["openai"],
|
|
143
|
+
});
|
|
144
|
+
const completion = await llm.createChatCompletion({
|
|
145
|
+
messages: prompt,
|
|
146
|
+
modelParameters: {
|
|
147
|
+
max_completion_tokens: constants_1.DEFAULT_O1_MODEL_PARAMETERS.max_completion_tokens,
|
|
148
|
+
reasoning_effort: "high",
|
|
149
|
+
tool_choice: "required",
|
|
150
|
+
},
|
|
151
|
+
trace: promptSpan,
|
|
152
|
+
tools: getPlannerToolCalls(),
|
|
153
|
+
});
|
|
154
|
+
promptSpan?.end({ output: { completion } });
|
|
155
|
+
if (!completion?.tool_calls || completion?.tool_calls?.length === 0) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (completion.tool_calls[0].function.name === "exit") {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (completion.tool_calls[0].function.name === "change_plan") {
|
|
162
|
+
const args = completion.tool_calls[0].function.arguments;
|
|
163
|
+
const plan = (0, utils_1.parseJson)(args).plan;
|
|
164
|
+
return plan;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function systemPromptBuilderForRepoEdit(files) {
|
|
168
|
+
const prompt = `
|
|
169
|
+
You are an expert QA automation engineer well versed in Playwright and Typescript and is given an objective to update tests in the repository on the basis of task provided.
|
|
170
|
+
|
|
171
|
+
Directory structure for the repository:
|
|
172
|
+
└── /
|
|
173
|
+
├── tests/
|
|
174
|
+
├────test-data/
|
|
175
|
+
│ └── index.ts
|
|
176
|
+
├── pages/
|
|
177
|
+
├── playwright.config.ts
|
|
178
|
+
├── app_knowledge.md
|
|
179
|
+
├── .eslintrc.js
|
|
180
|
+
├── package.json
|
|
181
|
+
└── tsconfig.json
|
|
182
|
+
|
|
183
|
+
Explanation of repository structure:
|
|
184
|
+
- tests: this is a directory where all tests and fixtures are kept. All spec files here end with ".spec.ts" as naming convention. There is an exception for Playwright fixtures file. Fixtures file is named as "fixtures.ts"
|
|
185
|
+
- test-data: this is a directory where all the test data are kept.
|
|
186
|
+
- pages: this is a directory where all reusable page object model methods are kept. Page object model methods are written in a pure functional convention. These methods are reusable methods created to be used across tests. This is also a directory where all the utility methods are kept which can be executed independent of the Playwright tests.
|
|
187
|
+
|
|
188
|
+
Coding principles and guidelines:
|
|
189
|
+
|
|
190
|
+
## Code Style and Structure
|
|
191
|
+
- Write concise, maintainable, and technically accurate TypeScript code with relevant examples.
|
|
192
|
+
- Use functional and declarative programming patterns; avoid classes.
|
|
193
|
+
- Use types to describe the shape of data and the behavior of functions.
|
|
194
|
+
- Favour iteration and modularisation to adhere to DRY principles and avoid code duplication.
|
|
195
|
+
- Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError).
|
|
196
|
+
- Organise files systematically: each file should contain only related content, such as exported components, subcomponents, helpers, static content, and types.
|
|
197
|
+
- Page object models are pure functions and are stateless.
|
|
198
|
+
|
|
199
|
+
## Naming Conventions
|
|
200
|
+
- Use lowercase with dashes for directories (e.g., tests/auth-wizard).
|
|
201
|
+
- Favour named exports for functions.
|
|
202
|
+
|
|
203
|
+
## TypeScript Usage
|
|
204
|
+
- Use TypeScript for all code; prefer interfaces over types for their extendability and ability to merge.
|
|
205
|
+
- Avoid enums; use maps instead for better type safety and flexibility.
|
|
206
|
+
- Use functional components with TypeScript interfaces.
|
|
207
|
+
|
|
208
|
+
## Syntax and Formatting
|
|
209
|
+
- Use the "function" keyword for pure functions to benefit from hoisting and clarity.
|
|
210
|
+
|
|
211
|
+
You will also be provided with current test files, fixtures and page object models for you to use and update code as per the task provided to you.
|
|
212
|
+
|
|
213
|
+
Here is the list of the files in the repository:
|
|
214
|
+
${files}
|
|
215
|
+
`;
|
|
216
|
+
return prompt;
|
|
217
|
+
}
|
|
218
|
+
exports.systemPromptBuilderForRepoEdit = systemPromptBuilderForRepoEdit;
|
|
219
|
+
function userPromptBuilderForStrReplace(plan) {
|
|
220
|
+
const prompt = `
|
|
221
|
+
You need to work to achieve the plan provided to you.
|
|
222
|
+
|
|
223
|
+
Plan:
|
|
224
|
+
${plan}
|
|
225
|
+
|
|
226
|
+
In order to execute the plan, FOLLOW BELOW STEPS:
|
|
227
|
+
- First go through the files in the repository and understand the code and the dependencies.
|
|
228
|
+
- Read the plan and figure out what are the changes that needs to be made.
|
|
229
|
+
- List down the changes required to execute the plan.
|
|
230
|
+
- Pick each change and identify the file path which needs the change, the reason for change and the code change they need.
|
|
231
|
+
- Use 'create_file' tool to create a new file if the file doesn't exist.
|
|
232
|
+
- Use separate 'str_replace' tool to make the changes for each update, even if the changes are in the same file.
|
|
233
|
+
- You can provide multiple 'str_replace' tool calls in the response.
|
|
234
|
+
- After making the changes check if the changes are made to all the files if not apply the changes to the remaining files.
|
|
235
|
+
- Also ensure that no extra changes are made to the code.
|
|
236
|
+
|
|
237
|
+
Notes for using the 'str_replace' command:
|
|
238
|
+
* The 'oldCode' parameter should match EXACTLY one or more consecutive lines from the original file. Be mindful of whitespaces!
|
|
239
|
+
* If the 'oldCode' parameter is not unique in the file, the replacement will not be performed. Make sure to include enough context in 'oldCode' to make it unique
|
|
240
|
+
* The 'newCode' parameter should contain the edited lines that should replace the 'oldCode'
|
|
241
|
+
|
|
242
|
+
Notes for using the 'create_file' command:
|
|
243
|
+
* The 'create_file' command cannot be used if the specified 'filePath' already exists as a file.
|
|
244
|
+
`;
|
|
245
|
+
return prompt;
|
|
246
|
+
}
|
|
247
|
+
function userPromptBuilderForPlanner(task) {
|
|
248
|
+
return `
|
|
249
|
+
You need to work towards completing the task provided to you.
|
|
250
|
+
|
|
251
|
+
Task:
|
|
252
|
+
${task}
|
|
253
|
+
|
|
254
|
+
In order to execute the task, FOLLOW BELOW STEPS:
|
|
255
|
+
- First go through the files in the repository and understand the code and the dependencies.
|
|
256
|
+
- Read the task and figure out what are the changes that needs to be made.
|
|
257
|
+
- List down the changes required for the given task.
|
|
258
|
+
- If there are no changes to be made, respond with "exit" tool call to exit the conversation.
|
|
259
|
+
- Craft a prompt for the agent to make the changes to the code.
|
|
260
|
+
- The plan should be readable and easy to understand.
|
|
261
|
+
- The plan should contain the details of the changes to be made such as file path, test name etc.
|
|
262
|
+
- If there are multiple changes in the same file, mention those separately in the plan.
|
|
263
|
+
`;
|
|
264
|
+
}
|
|
265
|
+
function deDupUpdatedFiles(updatedFiles) {
|
|
266
|
+
return updatedFiles.filter((change, index, self) => index ===
|
|
267
|
+
self.findIndex((existing) => existing.filePath === change.filePath &&
|
|
268
|
+
existing.oldCode === change.oldCode &&
|
|
269
|
+
existing.newCode === change.newCode));
|
|
270
|
+
}
|
|
271
|
+
async function generateCodeAndApplyChanges({ task, trace, logger, getRelevantFiles, }) {
|
|
272
|
+
let planRetries = 5;
|
|
273
|
+
let updatedFiles = [];
|
|
274
|
+
while (planRetries--) {
|
|
275
|
+
const generateCodeAndApplyChangesSpan = trace?.span({
|
|
276
|
+
name: "generate-code-apply-changes",
|
|
277
|
+
input: {
|
|
278
|
+
task,
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
const { prompt: files } = await getRelevantFiles();
|
|
282
|
+
if (!files) {
|
|
283
|
+
return deDupUpdatedFiles(updatedFiles);
|
|
284
|
+
}
|
|
285
|
+
let strReplacePlan = await getPlanForCodeEditorAgent({
|
|
286
|
+
prompt: [
|
|
287
|
+
{
|
|
288
|
+
role: "system",
|
|
289
|
+
content: systemPromptBuilderForRepoEdit(files),
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
role: "user",
|
|
293
|
+
content: userPromptBuilderForPlanner(task),
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
trace: generateCodeAndApplyChangesSpan,
|
|
297
|
+
logger,
|
|
298
|
+
});
|
|
299
|
+
if (!strReplacePlan) {
|
|
300
|
+
await (0, llm_1.flushAllTraces)();
|
|
301
|
+
return deDupUpdatedFiles(updatedFiles);
|
|
302
|
+
}
|
|
303
|
+
let strReplaceRetries = 3;
|
|
304
|
+
while (strReplaceRetries--) {
|
|
305
|
+
const promptForStrReplace = [
|
|
306
|
+
{
|
|
307
|
+
role: "system",
|
|
308
|
+
content: systemPromptBuilderForRepoEdit(files),
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
role: "user",
|
|
312
|
+
content: userPromptBuilderForStrReplace(strReplacePlan),
|
|
313
|
+
},
|
|
314
|
+
];
|
|
315
|
+
const codeEditorSpan = generateCodeAndApplyChangesSpan?.span({
|
|
316
|
+
name: "code-editor-agent",
|
|
317
|
+
input: {
|
|
318
|
+
prompt: promptForStrReplace,
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
const llm = new llm_1.LLM({
|
|
322
|
+
trace: codeEditorSpan,
|
|
323
|
+
provider: "anthropic",
|
|
324
|
+
defaultModel: "claude-3-5-sonnet-20240620",
|
|
325
|
+
providerApiKey: constants_1.MODEL_API_KEYS["anthropic"],
|
|
326
|
+
});
|
|
327
|
+
const completion = await llm.createChatCompletion({
|
|
328
|
+
messages: promptForStrReplace,
|
|
329
|
+
modelParameters: {
|
|
330
|
+
...constants_1.DEFAULT_MODEL_PARAMETERS,
|
|
331
|
+
temperature: 0.1,
|
|
332
|
+
tool_choice: "required",
|
|
333
|
+
},
|
|
334
|
+
trace: codeEditorSpan,
|
|
335
|
+
tools: getCodeEditorToolCalls(),
|
|
336
|
+
});
|
|
337
|
+
codeEditorSpan?.end({ output: { completion } });
|
|
338
|
+
if (!completion?.tool_calls || completion?.tool_calls?.length === 0) {
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
let codeEditorToolCalls = completion.tool_calls;
|
|
342
|
+
// Filter out the tool calls which are for creating new files
|
|
343
|
+
const createFileToolCalls = completion.tool_calls.filter((tc) => tc.function.name === types_1.CodeEditorToolCall.CREATE_FILE);
|
|
344
|
+
if (createFileToolCalls.length > 0) {
|
|
345
|
+
console.log(`create_file tool calls: `, createFileToolCalls);
|
|
346
|
+
}
|
|
347
|
+
await Promise.all(createFileToolCalls.map((tc) => {
|
|
348
|
+
return (async () => {
|
|
349
|
+
const args = (0, utils_1.parseJson)(tc.function.arguments);
|
|
350
|
+
updatedFiles.push({
|
|
351
|
+
filePath: args.filePath,
|
|
352
|
+
oldCode: "",
|
|
353
|
+
newCode: args.code,
|
|
354
|
+
reason: args.reason,
|
|
355
|
+
});
|
|
356
|
+
await fs_extra_1.default.mkdir((0, path_1.dirname)(args.filePath), { recursive: true });
|
|
357
|
+
await fs_extra_1.default.writeFile(args.filePath, args.code, "utf-8");
|
|
358
|
+
console.log(`Created file: ${args.filePath}`);
|
|
359
|
+
})();
|
|
360
|
+
}));
|
|
361
|
+
const strReplaceToolCalls = completion.tool_calls.filter((tc) => tc.function.name === types_1.CodeEditorToolCall.STR_REPLACE);
|
|
362
|
+
if (strReplaceToolCalls.length > 0) {
|
|
363
|
+
console.log(`str_replace tool calls: `, strReplaceToolCalls);
|
|
364
|
+
}
|
|
365
|
+
// Filter out the tool calls which are for replacing code in existing files
|
|
366
|
+
const fileChanges = strReplaceToolCalls
|
|
367
|
+
.map((toolCall) => (0, utils_1.parseJson)(toolCall.function.arguments))
|
|
368
|
+
.filter((f) => f.filePath && fs_extra_1.default.existsSync(f.filePath));
|
|
369
|
+
updatedFiles.push(...fileChanges);
|
|
370
|
+
let failedCodeUpdates;
|
|
371
|
+
// applyChangesResponse contains the errors occurred while applying the changes
|
|
372
|
+
failedCodeUpdates = await (0, utils_2.applyFileChangesUsingStrReplace)({
|
|
373
|
+
trace: codeEditorSpan,
|
|
374
|
+
fileChanges,
|
|
375
|
+
logger,
|
|
376
|
+
});
|
|
377
|
+
// Filter out the responses having errors
|
|
378
|
+
failedCodeUpdates = failedCodeUpdates.filter((f) => f?.error);
|
|
379
|
+
// Filter out the tool calls which have errors
|
|
380
|
+
const toolCallsWithErrors = codeEditorToolCalls.filter((toolCall) => {
|
|
381
|
+
const args = (0, utils_1.parseJson)(toolCall.function.arguments);
|
|
382
|
+
return failedCodeUpdates.find((response) => response.filePath === args.filePath);
|
|
383
|
+
});
|
|
384
|
+
if (failedCodeUpdates.length === 0) {
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
logger?.log(`Failed to apply changes, retrying...`, failedCodeUpdates);
|
|
388
|
+
const feedback = failedCodeUpdates
|
|
389
|
+
.map((updates) => `For file ${updates.filePath}: ${updates.errorMessage}`)
|
|
390
|
+
.join("\n");
|
|
391
|
+
promptForStrReplace.push({
|
|
392
|
+
role: "assistant",
|
|
393
|
+
tool_calls: toolCallsWithErrors,
|
|
394
|
+
});
|
|
395
|
+
toolCallsWithErrors.forEach((toolCall) => {
|
|
396
|
+
promptForStrReplace.push({
|
|
397
|
+
role: "tool",
|
|
398
|
+
tool_call_id: toolCall.id,
|
|
399
|
+
content: `
|
|
400
|
+
Errors while executing the changes provided in above tool call:
|
|
401
|
+
${feedback}
|
|
402
|
+
|
|
403
|
+
Please fix the errors and return the updated code.
|
|
404
|
+
|
|
405
|
+
FOLLOW BELOW STEPS TO FIX THE ISSUES:
|
|
406
|
+
- First read the error message and understand the issue.
|
|
407
|
+
- Go through the new code block and current file code, to figure out the root cause of the issue.
|
|
408
|
+
- Compile the steps that you need to follow to fix the issue.
|
|
409
|
+
- Check the test names, to ensure that the changes are applied to the correct test.
|
|
410
|
+
- Use separate 'str_replace' tool to make the changes for each update.
|
|
411
|
+
- Return the updated code in the same format as provided in the tool call.
|
|
412
|
+
|
|
413
|
+
NOTE: ONLY MAKE THE CHANGES TO FIX THE ISSUES MENTIONED IN THE ERROR MESSAGE AND NOTHING ELSE. NO EXTRA CODE REFACTORING IS REQUIRED.
|
|
414
|
+
`,
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return deDupUpdatedFiles(updatedFiles);
|
|
420
|
+
}
|
|
421
|
+
exports.generateCodeAndApplyChanges = generateCodeAndApplyChanges;
|
|
@@ -1,32 +1,25 @@
|
|
|
1
1
|
import { TraceClient } from "@empiricalrun/llm";
|
|
2
2
|
import { ChatCompletionMessageParam } from "openai/resources/index.mjs";
|
|
3
3
|
import { CustomLogger } from "../../bin/logger";
|
|
4
|
-
|
|
4
|
+
import { CodeUpdate } from "./types";
|
|
5
|
+
export declare function generateCodeUsingRepoAgent({ task, trace, repoFiles, useStrReplace, logger, }: {
|
|
5
6
|
trace?: TraceClient;
|
|
6
7
|
task: string;
|
|
7
|
-
repoFiles
|
|
8
|
+
repoFiles: string;
|
|
9
|
+
useStrReplace?: boolean;
|
|
10
|
+
logger?: CustomLogger;
|
|
8
11
|
}): Promise<{
|
|
9
12
|
prompt: ChatCompletionMessageParam[];
|
|
10
13
|
agentResponse: string;
|
|
11
|
-
fileChanges:
|
|
12
|
-
filePath: string | undefined;
|
|
13
|
-
oldCode: string | undefined;
|
|
14
|
-
newCode: string | undefined;
|
|
15
|
-
reason: string | undefined;
|
|
16
|
-
}[];
|
|
14
|
+
fileChanges: CodeUpdate[];
|
|
17
15
|
}>;
|
|
18
|
-
export declare function repoEditAgent({ trace, task, logger, }: {
|
|
16
|
+
export declare function repoEditAgent({ trace, task, logger, useStrReplace, }: {
|
|
19
17
|
trace?: TraceClient;
|
|
20
18
|
task: string;
|
|
21
19
|
logger?: CustomLogger;
|
|
20
|
+
useStrReplace?: boolean;
|
|
22
21
|
}): Promise<{
|
|
23
22
|
prompt: ChatCompletionMessageParam[];
|
|
24
|
-
|
|
25
|
-
fileChanges: {
|
|
26
|
-
filePath: string | undefined;
|
|
27
|
-
oldCode: string | undefined;
|
|
28
|
-
newCode: string | undefined;
|
|
29
|
-
reason: string | undefined;
|
|
30
|
-
}[];
|
|
23
|
+
fileChanges: CodeUpdate[];
|
|
31
24
|
}>;
|
|
32
25
|
//# sourceMappingURL=repo-edit.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repo-edit.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/repo-edit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAGxE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"repo-edit.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/repo-edit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAGxE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAWhD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAOrC,wBAAsB,0BAA0B,CAAC,EAC/C,IAAI,EACJ,KAAK,EACL,SAAS,EACT,aAAa,EACb,MAAM,GACP,EAAE;IACD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,GAAG,OAAO,CAAC;IACV,MAAM,EAAE,0BAA0B,EAAE,CAAC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B,CAAC,CAwJD;AAED,wBAAsB,aAAa,CAAC,EAClC,KAAK,EACL,IAAI,EACJ,MAAM,EACN,aAAa,GACd,EAAE;IACD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,GAAG,OAAO,CAAC;IACV,MAAM,EAAE,0BAA0B,EAAE,CAAC;IACrC,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B,CAAC,CA0ED"}
|
|
@@ -11,14 +11,55 @@ const context_1 = require("../../bin/utils/context");
|
|
|
11
11
|
const web_1 = require("../../bin/utils/platform/web");
|
|
12
12
|
const constants_1 = require("../../constants");
|
|
13
13
|
const reporter_1 = require("../../reporter");
|
|
14
|
+
const generate_code_apply_changes_1 = require("./generate-code-apply-changes");
|
|
14
15
|
const test_update_feedback_1 = require("./test-update-feedback");
|
|
15
16
|
const utils_1 = require("./utils");
|
|
16
|
-
async function generateCodeUsingRepoAgent({ task, trace, repoFiles, }) {
|
|
17
|
+
async function generateCodeUsingRepoAgent({ task, trace, repoFiles, useStrReplace, logger, }) {
|
|
17
18
|
const repoEditSpan = trace?.span({
|
|
18
19
|
name: "repo-edit",
|
|
19
20
|
input: { task },
|
|
20
21
|
});
|
|
21
|
-
// TODO:
|
|
22
|
+
// TODO: remove this once we test this flow
|
|
23
|
+
if (useStrReplace) {
|
|
24
|
+
const prompt = [
|
|
25
|
+
{
|
|
26
|
+
role: "system",
|
|
27
|
+
content: (0, generate_code_apply_changes_1.systemPromptBuilderForRepoEdit)(repoFiles),
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
role: "user",
|
|
31
|
+
content: `
|
|
32
|
+
You need to work towards completing the task provided to you.
|
|
33
|
+
|
|
34
|
+
Task:
|
|
35
|
+
${task}
|
|
36
|
+
|
|
37
|
+
In order to execute the task, FOLLOW BELOW STEPS:
|
|
38
|
+
- First go through the files in the repository and understand the code and the dependencies.
|
|
39
|
+
- Read the task and figure out what are the changes that needs to be made.
|
|
40
|
+
- List down the changes required for the given task.
|
|
41
|
+
- Create a clear, detailed plan which will be used as a prompt for the code change agent. Include specifics like file paths and test names. If there are multiple changes in one file, list them separately.
|
|
42
|
+
- The plan should be readable and easy to understand.
|
|
43
|
+
|
|
44
|
+
If all the changes are done, respond with "exit" tool call otherwise respond with "change-plan" tool call.
|
|
45
|
+
`,
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
const fileChanges = await (0, generate_code_apply_changes_1.generateCodeAndApplyChanges)({
|
|
49
|
+
task,
|
|
50
|
+
trace: repoEditSpan,
|
|
51
|
+
getRelevantFiles: async () => await (0, context_1.generateTxtForRepository)(),
|
|
52
|
+
logger,
|
|
53
|
+
});
|
|
54
|
+
repoEditSpan?.end({
|
|
55
|
+
output: { fileChanges },
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
prompt,
|
|
59
|
+
fileChanges,
|
|
60
|
+
agentResponse: "",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
22
63
|
const prompt = [
|
|
23
64
|
{
|
|
24
65
|
role: "system",
|
|
@@ -122,7 +163,7 @@ Task: ${task}
|
|
|
122
163
|
};
|
|
123
164
|
}
|
|
124
165
|
exports.generateCodeUsingRepoAgent = generateCodeUsingRepoAgent;
|
|
125
|
-
async function repoEditAgent({ trace, task, logger, }) {
|
|
166
|
+
async function repoEditAgent({ trace, task, logger, useStrReplace, }) {
|
|
126
167
|
const testgenUpdatesReporter = new reporter_1.TestGenUpdatesReporter();
|
|
127
168
|
void testgenUpdatesReporter.sendMessage(`Updating test code as per the task. \n View [trace](${trace?.getTraceUrl()})`);
|
|
128
169
|
logger?.log(`Starting repo agent: [trace](${trace?.getTraceUrl()})`);
|
|
@@ -130,36 +171,40 @@ async function repoEditAgent({ trace, task, logger, }) {
|
|
|
130
171
|
const repoAgentOutput = await generateCodeUsingRepoAgent({
|
|
131
172
|
task,
|
|
132
173
|
trace,
|
|
133
|
-
repoFiles,
|
|
174
|
+
repoFiles: repoFiles,
|
|
175
|
+
useStrReplace,
|
|
176
|
+
logger,
|
|
134
177
|
});
|
|
135
178
|
const updates = repoAgentOutput.fileChanges;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
fileChanges: updates.filter((f) => f.filePath && fs_extra_1.default.existsSync(f.filePath)),
|
|
139
|
-
});
|
|
140
|
-
const errorResponses = fileUpdateResponses.filter((f) => f?.error);
|
|
141
|
-
if (errorResponses.length > 0) {
|
|
142
|
-
const updatedFileChanges = await (0, test_update_feedback_1.applyTestUpdateFeedbacks)({
|
|
143
|
-
trace,
|
|
144
|
-
oldPrompt: repoAgentOutput.prompt,
|
|
145
|
-
agentResponse: repoAgentOutput.agentResponse,
|
|
146
|
-
feedbacks: errorResponses.map((e) => ({
|
|
147
|
-
filePath: e?.filePath,
|
|
148
|
-
errorMessage: e?.errorMessage,
|
|
149
|
-
})),
|
|
150
|
-
});
|
|
151
|
-
await (0, utils_1.applyFileChangesForRepoEdit)({
|
|
179
|
+
if (!useStrReplace) {
|
|
180
|
+
const fileUpdateResponses = await (0, utils_1.applyFileChangesForRepoEdit)({
|
|
152
181
|
trace,
|
|
153
|
-
fileChanges:
|
|
182
|
+
fileChanges: updates.filter((f) => f.filePath && fs_extra_1.default.existsSync(f.filePath)),
|
|
154
183
|
});
|
|
184
|
+
const errorResponses = fileUpdateResponses.filter((f) => f?.error);
|
|
185
|
+
if (errorResponses.length > 0) {
|
|
186
|
+
const updatedFileChanges = await (0, test_update_feedback_1.applyTestUpdateFeedbacks)({
|
|
187
|
+
trace,
|
|
188
|
+
oldPrompt: repoAgentOutput.prompt,
|
|
189
|
+
agentResponse: repoAgentOutput.agentResponse,
|
|
190
|
+
feedbacks: errorResponses.map((e) => ({
|
|
191
|
+
filePath: e?.filePath,
|
|
192
|
+
errorMessage: e?.errorMessage,
|
|
193
|
+
})),
|
|
194
|
+
});
|
|
195
|
+
await (0, utils_1.applyFileChangesForRepoEdit)({
|
|
196
|
+
trace,
|
|
197
|
+
fileChanges: updatedFileChanges,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const newFileUpdates = updates.filter((f) => !fs_extra_1.default.existsSync(f.filePath));
|
|
201
|
+
await Promise.all(newFileUpdates.map((f) => {
|
|
202
|
+
return (async () => {
|
|
203
|
+
await fs_extra_1.default.mkdir((0, path_1.dirname)(f.filePath), { recursive: true });
|
|
204
|
+
await fs_extra_1.default.writeFile(f.filePath, f.newCode, "utf-8");
|
|
205
|
+
})();
|
|
206
|
+
}));
|
|
155
207
|
}
|
|
156
|
-
const newFileUpdates = updates.filter((f) => !fs_extra_1.default.existsSync(f.filePath));
|
|
157
|
-
await Promise.all(newFileUpdates.map((f) => {
|
|
158
|
-
return (async () => {
|
|
159
|
-
await fs_extra_1.default.mkdir((0, path_1.dirname)(f.filePath), { recursive: true });
|
|
160
|
-
await fs_extra_1.default.writeFile(f.filePath, f.newCode, "utf-8");
|
|
161
|
-
})();
|
|
162
|
-
}));
|
|
163
208
|
await (0, utils_1.validateTypesAndFormatCode)({
|
|
164
209
|
validateTypes: true,
|
|
165
210
|
trace,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAMhE,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAK7D,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,EAC7B,KAAK,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAMhE,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAK7D,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,EAC7B,KAAK,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,CAsC5B"}
|
|
@@ -27,6 +27,7 @@ async function generateTest(testCase, file, options, trace) {
|
|
|
27
27
|
});
|
|
28
28
|
const isUpdate = !!testBlock;
|
|
29
29
|
if (isUpdate) {
|
|
30
|
+
logger.log(`Updating the test '${testCase.name}': [View trace](${trace?.getTraceUrl()})`);
|
|
30
31
|
return await (0, update_flow_1.updateTest)(testCase, file, options, true, true, trace);
|
|
31
32
|
}
|
|
32
33
|
const createTestSpan = trace?.span({
|
|
@@ -8,10 +8,5 @@ export declare function applyTestUpdateFeedbacks({ trace, oldPrompt, feedbacks,
|
|
|
8
8
|
errorMessage: string;
|
|
9
9
|
}[];
|
|
10
10
|
trace?: TraceClient;
|
|
11
|
-
}): Promise<
|
|
12
|
-
filePath: string | undefined;
|
|
13
|
-
oldCode: string | undefined;
|
|
14
|
-
newCode: string | undefined;
|
|
15
|
-
reason: string | undefined;
|
|
16
|
-
}[]>;
|
|
11
|
+
}): Promise<import("./types").CodeUpdate[]>;
|
|
17
12
|
//# sourceMappingURL=test-update-feedback.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-update-feedback.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/test-update-feedback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAKxE,wBAAsB,wBAAwB,CAAC,EAC7C,KAAK,EACL,SAAS,EACT,SAAS,EACT,aAAa,GACd,EAAE;IACD,SAAS,EAAE,0BAA0B,EAAE,CAAC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE;QACT,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,YAAY,EAAE,MAAM,CAAC;KACtB,EAAE,CAAC;IACJ,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB
|
|
1
|
+
{"version":3,"file":"test-update-feedback.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/test-update-feedback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAKxE,wBAAsB,wBAAwB,CAAC,EAC7C,KAAK,EACL,SAAS,EACT,SAAS,EACT,aAAa,GACd,EAAE;IACD,SAAS,EAAE,0BAA0B,EAAE,CAAC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE;QACT,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,YAAY,EAAE,MAAM,CAAC;KACtB,EAAE,CAAC;IACJ,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,2CA4CA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { TestCase } from "../../types";
|
|
2
|
+
export type CodeUpdate = {
|
|
3
|
+
filePath: string | undefined;
|
|
4
|
+
oldCode: string | undefined;
|
|
5
|
+
newCode: string | undefined;
|
|
6
|
+
reason: string | undefined;
|
|
7
|
+
};
|
|
8
|
+
export type FileUpdateResponse = {
|
|
9
|
+
error: boolean;
|
|
10
|
+
errorMessage: string;
|
|
11
|
+
filePath: string;
|
|
12
|
+
};
|
|
13
|
+
export type UpdatedTestCase = TestCase & {
|
|
14
|
+
updatedFiles: string[];
|
|
15
|
+
};
|
|
16
|
+
export declare enum CodeEditorToolCall {
|
|
17
|
+
STR_REPLACE = "str_replace",
|
|
18
|
+
CREATE_FILE = "create_file"
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAGF,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG;IACvC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,oBAAY,kBAAkB;IAC5B,WAAW,gBAAgB;IAC3B,WAAW,gBAAgB;CAC5B"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CodeEditorToolCall = void 0;
|
|
4
|
+
var CodeEditorToolCall;
|
|
5
|
+
(function (CodeEditorToolCall) {
|
|
6
|
+
CodeEditorToolCall["STR_REPLACE"] = "str_replace";
|
|
7
|
+
CodeEditorToolCall["CREATE_FILE"] = "create_file";
|
|
8
|
+
})(CodeEditorToolCall || (exports.CodeEditorToolCall = CodeEditorToolCall = {}));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { TraceClient } from "@empiricalrun/llm";
|
|
2
2
|
import { ChatCompletionMessageParam } from "openai/resources/index.mjs";
|
|
3
3
|
import { TestCase, TestGenConfigOptions } from "../../types";
|
|
4
|
-
import { UpdatedTestCase } from "./
|
|
5
|
-
export declare function getUpdateTestCodeCompletion({ testCase, testFileContent, testFiles, pageFiles, testFilePath,
|
|
4
|
+
import { CodeUpdate, UpdatedTestCase } from "./types";
|
|
5
|
+
export declare function getUpdateTestCodeCompletion({ testCase, testFileContent, testFiles, pageFiles, testFilePath, options, trace, }: {
|
|
6
6
|
testCase: TestCase;
|
|
7
7
|
testFiles: string;
|
|
8
8
|
pageFiles: string;
|
|
@@ -13,12 +13,7 @@ export declare function getUpdateTestCodeCompletion({ testCase, testFileContent,
|
|
|
13
13
|
}): Promise<{
|
|
14
14
|
prompt: ChatCompletionMessageParam[];
|
|
15
15
|
agentResponse: string;
|
|
16
|
-
fileChanges:
|
|
17
|
-
filePath: string | undefined;
|
|
18
|
-
oldCode: string | undefined;
|
|
19
|
-
newCode: string | undefined;
|
|
20
|
-
reason: string | undefined;
|
|
21
|
-
}[];
|
|
16
|
+
fileChanges: CodeUpdate[];
|
|
22
17
|
}>;
|
|
23
18
|
export declare function updateTest(testCase: TestCase, file: string, options: TestGenConfigOptions | undefined, logging?: boolean, validate?: boolean, trace?: TraceClient): Promise<UpdatedTestCase[]>;
|
|
24
19
|
export declare function getAppendCreateTestBlockCompletion({ testFiles, pageFiles, testCase, testFilePath, options, trace, }: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-flow.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/update-flow.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAYxE,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,
|
|
1
|
+
{"version":3,"file":"update-flow.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/update-flow.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAYxE,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAQtD,wBAAsB,2BAA2B,CAAC,EAChD,QAAQ,EACR,eAAe,EACf,SAAS,EACT,SAAS,EACT,YAAY,EACZ,OAAO,EACP,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC,GAAG,OAAO,CAAC;IACV,MAAM,EAAE,0BAA0B,EAAE,CAAC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B,CAAC,CAmDD;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAAG,SAAS,EACzC,OAAO,GAAE,OAAc,EACvB,QAAQ,GAAE,OAAc,EACxB,KAAK,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,eAAe,EAAE,CAAC,CA+F5B;AAED,wBAAsB,kCAAkC,CAAC,EACvD,SAAS,EACT,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,KAAK,GACN,EAAE;IACD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;CACtB,mBAuGA;AAED,wBAAsB,qBAAqB,CAAC,EAC1C,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,KAAK,EACL,aAAoB,GACrB,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAqD7B"}
|
|
@@ -15,11 +15,10 @@ const constants_1 = require("../../constants");
|
|
|
15
15
|
const session_1 = require("../../session");
|
|
16
16
|
const test_update_feedback_1 = require("./test-update-feedback");
|
|
17
17
|
const utils_1 = require("./utils");
|
|
18
|
-
async function getUpdateTestCodeCompletion({ testCase, testFileContent, testFiles, pageFiles, testFilePath,
|
|
18
|
+
async function getUpdateTestCodeCompletion({ testCase, testFileContent, testFiles, pageFiles, testFilePath, options, trace, }) {
|
|
19
19
|
const promptSpan = trace?.span({
|
|
20
20
|
name: "update-scenario-prompt",
|
|
21
21
|
});
|
|
22
|
-
const promptName = "update-scenario";
|
|
23
22
|
// if describe blocks are present, we need to add them to the scenario name
|
|
24
23
|
// e.g. describe block: login ---> login with email
|
|
25
24
|
// this is help LLM navigate to the right test block
|
|
@@ -32,6 +31,7 @@ async function getUpdateTestCodeCompletion({ testCase, testFileContent, testFile
|
|
|
32
31
|
content: testFileContent,
|
|
33
32
|
suites: testCase?.suites || [],
|
|
34
33
|
});
|
|
34
|
+
const promptName = "update-scenario";
|
|
35
35
|
const prompt = await (0, llm_1.getPrompt)(promptName, {
|
|
36
36
|
testFiles: testFiles,
|
|
37
37
|
pageFiles: pageFiles,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TraceClient } from "@empiricalrun/llm";
|
|
2
2
|
import { CustomLogger } from "../../bin/logger";
|
|
3
3
|
import { TestCase, TestGenConfigOptions } from "../../types";
|
|
4
|
+
import { CodeUpdate, FileUpdateResponse } from "./types";
|
|
4
5
|
/**
|
|
5
6
|
*
|
|
6
7
|
* method to extract file path and code updates for the LLM response of update flow
|
|
@@ -13,12 +14,7 @@ import { TestCase, TestGenConfigOptions } from "../../types";
|
|
|
13
14
|
* reason: string | undefined;
|
|
14
15
|
* }[])}
|
|
15
16
|
*/
|
|
16
|
-
export declare function extractTestUpdates(input: string):
|
|
17
|
-
filePath: string | undefined;
|
|
18
|
-
oldCode: string | undefined;
|
|
19
|
-
newCode: string | undefined;
|
|
20
|
-
reason: string | undefined;
|
|
21
|
-
}[];
|
|
17
|
+
export declare function extractTestUpdates(input: string): CodeUpdate[];
|
|
22
18
|
/**
|
|
23
19
|
*
|
|
24
20
|
* method to extract append create test updates
|
|
@@ -48,37 +44,11 @@ export declare function extractTestStepsSuggestions(input: string): {
|
|
|
48
44
|
reason: string;
|
|
49
45
|
methodName: string;
|
|
50
46
|
}[];
|
|
51
|
-
export type UpdatedTestCase = TestCase & {
|
|
52
|
-
updatedFiles: string[];
|
|
53
|
-
};
|
|
54
|
-
export declare function applyFileChanges({ trace, testCase, fileChanges, logger, }: {
|
|
55
|
-
trace?: TraceClient;
|
|
56
|
-
testCase: TestCase;
|
|
57
|
-
fileChanges: {
|
|
58
|
-
filePath: string | undefined;
|
|
59
|
-
oldCode: string | undefined;
|
|
60
|
-
newCode: string | undefined;
|
|
61
|
-
reason: string | undefined;
|
|
62
|
-
}[];
|
|
63
|
-
logger?: CustomLogger;
|
|
64
|
-
testGenOptions?: TestGenConfigOptions;
|
|
65
|
-
pomPrompt?: string;
|
|
66
|
-
nonSpecFilePrompt?: string;
|
|
67
|
-
}): Promise<{
|
|
68
|
-
error: boolean;
|
|
69
|
-
errorMessage: string;
|
|
70
|
-
filePath: string;
|
|
71
|
-
}[]>;
|
|
72
47
|
export declare function validateTypesAndFormatCode({ validateTypes, trace, testCase, fileChanges, logger, testGenOptions, pomPrompt, nonSpecFilePrompt, }: {
|
|
73
48
|
validateTypes?: boolean;
|
|
74
49
|
trace?: TraceClient;
|
|
75
50
|
testCase: TestCase;
|
|
76
|
-
fileChanges:
|
|
77
|
-
filePath: string | undefined;
|
|
78
|
-
oldCode: string | undefined;
|
|
79
|
-
newCode: string | undefined;
|
|
80
|
-
reason: string | undefined;
|
|
81
|
-
}[];
|
|
51
|
+
fileChanges: CodeUpdate[];
|
|
82
52
|
logger?: CustomLogger;
|
|
83
53
|
testGenOptions?: TestGenConfigOptions;
|
|
84
54
|
pomPrompt?: string;
|
|
@@ -88,6 +58,23 @@ export declare function getTaskForCreateTest({ testCase, file, }: {
|
|
|
88
58
|
testCase: TestCase;
|
|
89
59
|
file: string;
|
|
90
60
|
}): string;
|
|
61
|
+
export declare function applyFileChangesUsingStrReplace({ trace, fileChanges, logger, }: {
|
|
62
|
+
trace?: TraceClient;
|
|
63
|
+
fileChanges: CodeUpdate[];
|
|
64
|
+
logger?: CustomLogger;
|
|
65
|
+
}): Promise<FileUpdateResponse[]>;
|
|
66
|
+
export declare function searchAndReplaceCodeUsingStrReplace({ logger, fileChange, }: {
|
|
67
|
+
fileChange: {
|
|
68
|
+
filePath: string | undefined;
|
|
69
|
+
oldCode: string | undefined;
|
|
70
|
+
newCode: string | undefined;
|
|
71
|
+
reason: string | undefined;
|
|
72
|
+
};
|
|
73
|
+
logger?: CustomLogger;
|
|
74
|
+
}): Promise<{
|
|
75
|
+
result: FileUpdateResponse;
|
|
76
|
+
updatedContent: string;
|
|
77
|
+
}>;
|
|
91
78
|
export declare function applyFileChangesForRepoEdit({ trace, fileChanges, logger, }: {
|
|
92
79
|
trace?: TraceClient;
|
|
93
80
|
fileChanges: {
|
|
@@ -118,4 +105,22 @@ export declare function searchAndReplaceCode({ logger, fileChange, }: {
|
|
|
118
105
|
};
|
|
119
106
|
updatedContent: string;
|
|
120
107
|
}>;
|
|
108
|
+
export declare function applyFileChanges({ trace, testCase, fileChanges, logger, }: {
|
|
109
|
+
trace?: TraceClient;
|
|
110
|
+
testCase: TestCase;
|
|
111
|
+
fileChanges: {
|
|
112
|
+
filePath: string | undefined;
|
|
113
|
+
oldCode: string | undefined;
|
|
114
|
+
newCode: string | undefined;
|
|
115
|
+
reason: string | undefined;
|
|
116
|
+
}[];
|
|
117
|
+
logger?: CustomLogger;
|
|
118
|
+
testGenOptions?: TestGenConfigOptions;
|
|
119
|
+
pomPrompt?: string;
|
|
120
|
+
nonSpecFilePrompt?: string;
|
|
121
|
+
}): Promise<{
|
|
122
|
+
error: boolean;
|
|
123
|
+
errorMessage: string;
|
|
124
|
+
filePath: string;
|
|
125
|
+
}[]>;
|
|
121
126
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIhE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMhD,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ 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.searchAndReplaceCode = exports.applyFileChangesForRepoEdit = exports.
|
|
6
|
+
exports.applyFileChanges = exports.searchAndReplaceCode = exports.applyFileChangesForRepoEdit = exports.searchAndReplaceCodeUsingStrReplace = exports.applyFileChangesUsingStrReplace = exports.getTaskForCreateTest = exports.validateTypesAndFormatCode = exports.extractTestStepsSuggestions = exports.extractAppendTestUpdates = exports.extractTestUpdates = void 0;
|
|
7
7
|
const llm_1 = require("@empiricalrun/llm");
|
|
8
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
9
|
const ts_morph_1 = require("ts-morph");
|
|
@@ -89,115 +89,6 @@ function extractTestStepsSuggestions(input) {
|
|
|
89
89
|
return result.filter((r) => !!r.filePath && !!r.usageExample);
|
|
90
90
|
}
|
|
91
91
|
exports.extractTestStepsSuggestions = extractTestStepsSuggestions;
|
|
92
|
-
async function applyFileChanges({ trace, testCase, fileChanges, logger, }) {
|
|
93
|
-
const results = [];
|
|
94
|
-
for (const fileChange of fileChanges) {
|
|
95
|
-
if (!fileChange.filePath) {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
try {
|
|
99
|
-
const hasTestCaseAsUpdateContext = !!testCase?.name;
|
|
100
|
-
let testBlockUpdate = undefined;
|
|
101
|
-
if (hasTestCaseAsUpdateContext) {
|
|
102
|
-
const applyFileChangesSpan = trace?.span({
|
|
103
|
-
name: "apply-file-changes",
|
|
104
|
-
});
|
|
105
|
-
const block = (0, web_1.getTypescriptTestBlock)({
|
|
106
|
-
scenarioName: testCase?.name,
|
|
107
|
-
content: fileChange.newCode || "",
|
|
108
|
-
suites: [], // // suites should be empty here since we ask LLM to send immediate parent AST node for the code update. so there won't be any nesting here, just the test block
|
|
109
|
-
});
|
|
110
|
-
testBlockUpdate = block.testBlock;
|
|
111
|
-
applyFileChangesSpan?.end({ output: { testBlock: testBlockUpdate } });
|
|
112
|
-
}
|
|
113
|
-
if (testBlockUpdate) {
|
|
114
|
-
// assuming the test case getting updated
|
|
115
|
-
// maintaining the previous accuracy of the test case update
|
|
116
|
-
const readWriteFileSpan = trace?.span({ name: "write-to-file" });
|
|
117
|
-
let contents = await fs_extra_1.default.readFile(fileChange.filePath, "utf-8");
|
|
118
|
-
const [prependContent, strippedContent] = await (0, web_1.stripAndPrependImports)(fileChange.newCode, testCase?.name);
|
|
119
|
-
let updatedContent = prependContent + contents + `\n\n${strippedContent}`;
|
|
120
|
-
const { testBlock } = (0, web_1.getTypescriptTestBlock)({
|
|
121
|
-
scenarioName: testCase?.name,
|
|
122
|
-
content: contents,
|
|
123
|
-
suites: testCase?.suites,
|
|
124
|
-
});
|
|
125
|
-
contents = contents.replace(testBlock, `\n\n${strippedContent}`);
|
|
126
|
-
updatedContent = prependContent + contents;
|
|
127
|
-
await fs_extra_1.default.writeFile(fileChange.filePath, updatedContent, "utf-8");
|
|
128
|
-
readWriteFileSpan?.end({ output: { updatedContent } });
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
const readWriteFileSpan = trace?.span({ name: "write-to-file" });
|
|
132
|
-
let contents = await fs_extra_1.default.readFile(fileChange.filePath, "utf-8");
|
|
133
|
-
const project = new ts_morph_1.Project();
|
|
134
|
-
const sourceFile = project.createSourceFile("updated-code.ts", fileChange.newCode);
|
|
135
|
-
const functions = sourceFile.getFunctions();
|
|
136
|
-
const checkForMethodSrc = project.createSourceFile("check-method.ts", `class A {
|
|
137
|
-
${fileChange.newCode}
|
|
138
|
-
}`);
|
|
139
|
-
const methods = checkForMethodSrc.getDescendantsOfKind(ts_morph_1.SyntaxKind.MethodDeclaration);
|
|
140
|
-
const originalSource = project.createSourceFile("current-code.ts", contents);
|
|
141
|
-
// if there is a single function update in the file
|
|
142
|
-
if (functions.length === 1 &&
|
|
143
|
-
functions[0]?.getText() === fileChange.newCode) {
|
|
144
|
-
const updatedCodeFuncNames = functions.map((f) => f.getName());
|
|
145
|
-
const funcName = updatedCodeFuncNames[0];
|
|
146
|
-
const matchingNodes = originalSource
|
|
147
|
-
.getDescendantsOfKind(ts_morph_1.SyntaxKind.FunctionDeclaration)
|
|
148
|
-
.filter((node) => node.getName() === funcName);
|
|
149
|
-
matchingNodes[0]?.replaceWithText(functions[0]?.getText());
|
|
150
|
-
contents = originalSource.getFullText();
|
|
151
|
-
}
|
|
152
|
-
else if (
|
|
153
|
-
// if there is a update in method of a class in the file
|
|
154
|
-
methods.length === 1 &&
|
|
155
|
-
methods[0]?.getText() === fileChange.newCode) {
|
|
156
|
-
const method = methods[0];
|
|
157
|
-
const funcName = method?.getName();
|
|
158
|
-
const matchingNodes = originalSource
|
|
159
|
-
.getDescendantsOfKind(ts_morph_1.SyntaxKind.MethodDeclaration)
|
|
160
|
-
.filter((node) => node.getName() === funcName);
|
|
161
|
-
matchingNodes[0]?.replaceWithText(method?.getText());
|
|
162
|
-
contents = originalSource.getFullText();
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
const { result, updatedContent } = await searchAndReplaceCode({
|
|
166
|
-
logger,
|
|
167
|
-
fileChange,
|
|
168
|
-
});
|
|
169
|
-
if (result.error) {
|
|
170
|
-
logger?.error(`Unable to find the code to update in ${fileChange.filePath}`);
|
|
171
|
-
results.push(result);
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
contents = updatedContent;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
await fs_extra_1.default.writeFile(fileChange.filePath, contents, "utf-8");
|
|
179
|
-
readWriteFileSpan?.end({ output: { contents } });
|
|
180
|
-
results.push({
|
|
181
|
-
filePath: fileChange.filePath,
|
|
182
|
-
error: false,
|
|
183
|
-
errorMessage: "",
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
catch (e) {
|
|
188
|
-
trace?.event({
|
|
189
|
-
name: "apply-file-changes-error",
|
|
190
|
-
output: {
|
|
191
|
-
filePath: fileChange.filePath,
|
|
192
|
-
error: e,
|
|
193
|
-
},
|
|
194
|
-
});
|
|
195
|
-
console.error(`Error while applying changes to file ${fileChange.filePath}`, e);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return results;
|
|
199
|
-
}
|
|
200
|
-
exports.applyFileChanges = applyFileChanges;
|
|
201
92
|
async function validateTypesAndFormatCode({ validateTypes = true, trace, testCase, fileChanges, logger, testGenOptions, pomPrompt, nonSpecFilePrompt, }) {
|
|
202
93
|
for (let fileChange of fileChanges) {
|
|
203
94
|
if (!fileChange.filePath) {
|
|
@@ -239,6 +130,87 @@ ${testCase.steps.join("\n")}
|
|
|
239
130
|
`;
|
|
240
131
|
}
|
|
241
132
|
exports.getTaskForCreateTest = getTaskForCreateTest;
|
|
133
|
+
async function applyFileChangesUsingStrReplace({ trace, fileChanges, logger, }) {
|
|
134
|
+
const repoEditFileChangesSpan = trace?.span({
|
|
135
|
+
name: "repo-edit-file-changes",
|
|
136
|
+
});
|
|
137
|
+
const results = [];
|
|
138
|
+
for (const fileChange of fileChanges) {
|
|
139
|
+
if (!fileChange.filePath) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const readWriteFileSpan = repoEditFileChangesSpan?.span({
|
|
144
|
+
name: "write-to-file",
|
|
145
|
+
input: {
|
|
146
|
+
fileChange,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
const { result, updatedContent } = await searchAndReplaceCode({
|
|
150
|
+
logger,
|
|
151
|
+
fileChange,
|
|
152
|
+
});
|
|
153
|
+
if (result.error) {
|
|
154
|
+
logger?.error(`Unable to find the code to update in ${result.filePath}, full error:`, result);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
await fs_extra_1.default.writeFile(fileChange.filePath, updatedContent, "utf-8");
|
|
158
|
+
readWriteFileSpan?.end({ output: { updatedContent } });
|
|
159
|
+
}
|
|
160
|
+
results.push(result);
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
trace?.event({
|
|
164
|
+
name: "repo-edit-file-changes-error",
|
|
165
|
+
output: {
|
|
166
|
+
filePath: fileChange.filePath,
|
|
167
|
+
error: e,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
console.error(`Error while applying changes to file ${fileChange.filePath}`, e);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return results;
|
|
174
|
+
}
|
|
175
|
+
exports.applyFileChangesUsingStrReplace = applyFileChangesUsingStrReplace;
|
|
176
|
+
async function searchAndReplaceCodeUsingStrReplace({ logger, fileChange, }) {
|
|
177
|
+
let contents = await fs_extra_1.default.readFile(fileChange.filePath, "utf-8");
|
|
178
|
+
if (contents.includes(fileChange.oldCode)) {
|
|
179
|
+
// Check for multiple instances of old code block
|
|
180
|
+
// If there are multiple instances, then we cannot safely determine which instance to replace
|
|
181
|
+
const firstIndex = contents.indexOf(fileChange.oldCode);
|
|
182
|
+
const lastIndex = contents.lastIndexOf(fileChange.oldCode);
|
|
183
|
+
if (firstIndex !== lastIndex) {
|
|
184
|
+
return {
|
|
185
|
+
result: {
|
|
186
|
+
error: true,
|
|
187
|
+
errorMessage: `Multiple instances of the code block found in file "${fileChange.filePath}". Cannot safely determine which instance to replace.`,
|
|
188
|
+
filePath: fileChange.filePath,
|
|
189
|
+
},
|
|
190
|
+
updatedContent: contents,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const updatedContent = contents.replace(fileChange.oldCode, `\n\n${fileChange.newCode}`);
|
|
194
|
+
return {
|
|
195
|
+
result: {
|
|
196
|
+
error: false,
|
|
197
|
+
errorMessage: "",
|
|
198
|
+
filePath: fileChange.filePath,
|
|
199
|
+
},
|
|
200
|
+
updatedContent,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
logger?.error(`Unable to find the code to update in ${fileChange.filePath}`);
|
|
204
|
+
return {
|
|
205
|
+
result: {
|
|
206
|
+
error: true,
|
|
207
|
+
errorMessage: `The content of "old_code_block" corresponding to file path "${fileChange.filePath}" did not match the current content of the file "${fileChange.filePath}"`,
|
|
208
|
+
filePath: fileChange.filePath,
|
|
209
|
+
},
|
|
210
|
+
updatedContent: contents,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
exports.searchAndReplaceCodeUsingStrReplace = searchAndReplaceCodeUsingStrReplace;
|
|
242
214
|
async function applyFileChangesForRepoEdit({ trace, fileChanges, logger, }) {
|
|
243
215
|
const repoEditFileChangesSpan = trace?.span({
|
|
244
216
|
name: "repo-edit-file-changes",
|
|
@@ -309,3 +281,112 @@ async function searchAndReplaceCode({ logger, fileChange, }) {
|
|
|
309
281
|
};
|
|
310
282
|
}
|
|
311
283
|
exports.searchAndReplaceCode = searchAndReplaceCode;
|
|
284
|
+
async function applyFileChanges({ trace, testCase, fileChanges, logger, }) {
|
|
285
|
+
const results = [];
|
|
286
|
+
for (const fileChange of fileChanges) {
|
|
287
|
+
if (!fileChange.filePath) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
const hasTestCaseAsUpdateContext = !!testCase?.name;
|
|
292
|
+
let testBlockUpdate = undefined;
|
|
293
|
+
if (hasTestCaseAsUpdateContext) {
|
|
294
|
+
const applyFileChangesSpan = trace?.span({
|
|
295
|
+
name: "apply-file-changes",
|
|
296
|
+
});
|
|
297
|
+
const block = (0, web_1.getTypescriptTestBlock)({
|
|
298
|
+
scenarioName: testCase?.name,
|
|
299
|
+
content: fileChange.newCode || "",
|
|
300
|
+
suites: [], // // suites should be empty here since we ask LLM to send immediate parent AST node for the code update. so there won't be any nesting here, just the test block
|
|
301
|
+
});
|
|
302
|
+
testBlockUpdate = block.testBlock;
|
|
303
|
+
applyFileChangesSpan?.end({ output: { testBlock: testBlockUpdate } });
|
|
304
|
+
}
|
|
305
|
+
if (testBlockUpdate) {
|
|
306
|
+
// assuming the test case getting updated
|
|
307
|
+
// maintaining the previous accuracy of the test case update
|
|
308
|
+
const readWriteFileSpan = trace?.span({ name: "write-to-file" });
|
|
309
|
+
let contents = await fs_extra_1.default.readFile(fileChange.filePath, "utf-8");
|
|
310
|
+
const [prependContent, strippedContent] = await (0, web_1.stripAndPrependImports)(fileChange.newCode, testCase?.name);
|
|
311
|
+
let updatedContent = prependContent + contents + `\n\n${strippedContent}`;
|
|
312
|
+
const { testBlock } = (0, web_1.getTypescriptTestBlock)({
|
|
313
|
+
scenarioName: testCase?.name,
|
|
314
|
+
content: contents,
|
|
315
|
+
suites: testCase?.suites,
|
|
316
|
+
});
|
|
317
|
+
contents = contents.replace(testBlock, `\n\n${strippedContent}`);
|
|
318
|
+
updatedContent = prependContent + contents;
|
|
319
|
+
await fs_extra_1.default.writeFile(fileChange.filePath, updatedContent, "utf-8");
|
|
320
|
+
readWriteFileSpan?.end({ output: { updatedContent } });
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
const readWriteFileSpan = trace?.span({ name: "write-to-file" });
|
|
324
|
+
let contents = await fs_extra_1.default.readFile(fileChange.filePath, "utf-8");
|
|
325
|
+
const project = new ts_morph_1.Project();
|
|
326
|
+
const sourceFile = project.createSourceFile("updated-code.ts", fileChange.newCode);
|
|
327
|
+
const functions = sourceFile.getFunctions();
|
|
328
|
+
const checkForMethodSrc = project.createSourceFile("check-method.ts", `class A {
|
|
329
|
+
${fileChange.newCode}
|
|
330
|
+
}`);
|
|
331
|
+
const methods = checkForMethodSrc.getDescendantsOfKind(ts_morph_1.SyntaxKind.MethodDeclaration);
|
|
332
|
+
const originalSource = project.createSourceFile("current-code.ts", contents);
|
|
333
|
+
// if there is a single function update in the file
|
|
334
|
+
if (functions.length === 1 &&
|
|
335
|
+
functions[0]?.getText() === fileChange.newCode) {
|
|
336
|
+
const updatedCodeFuncNames = functions.map((f) => f.getName());
|
|
337
|
+
const funcName = updatedCodeFuncNames[0];
|
|
338
|
+
const matchingNodes = originalSource
|
|
339
|
+
.getDescendantsOfKind(ts_morph_1.SyntaxKind.FunctionDeclaration)
|
|
340
|
+
.filter((node) => node.getName() === funcName);
|
|
341
|
+
matchingNodes[0]?.replaceWithText(functions[0]?.getText());
|
|
342
|
+
contents = originalSource.getFullText();
|
|
343
|
+
}
|
|
344
|
+
else if (
|
|
345
|
+
// if there is a update in method of a class in the file
|
|
346
|
+
methods.length === 1 &&
|
|
347
|
+
methods[0]?.getText() === fileChange.newCode) {
|
|
348
|
+
const method = methods[0];
|
|
349
|
+
const funcName = method?.getName();
|
|
350
|
+
const matchingNodes = originalSource
|
|
351
|
+
.getDescendantsOfKind(ts_morph_1.SyntaxKind.MethodDeclaration)
|
|
352
|
+
.filter((node) => node.getName() === funcName);
|
|
353
|
+
matchingNodes[0]?.replaceWithText(method?.getText());
|
|
354
|
+
contents = originalSource.getFullText();
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
const { result, updatedContent } = await searchAndReplaceCode({
|
|
358
|
+
logger,
|
|
359
|
+
fileChange,
|
|
360
|
+
});
|
|
361
|
+
if (result.error) {
|
|
362
|
+
logger?.error(`Unable to find the code to update in ${fileChange.filePath}`);
|
|
363
|
+
results.push(result);
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
contents = updatedContent;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
await fs_extra_1.default.writeFile(fileChange.filePath, contents, "utf-8");
|
|
371
|
+
readWriteFileSpan?.end({ output: { contents } });
|
|
372
|
+
results.push({
|
|
373
|
+
filePath: fileChange.filePath,
|
|
374
|
+
error: false,
|
|
375
|
+
errorMessage: "",
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch (e) {
|
|
380
|
+
trace?.event({
|
|
381
|
+
name: "apply-file-changes-error",
|
|
382
|
+
output: {
|
|
383
|
+
filePath: fileChange.filePath,
|
|
384
|
+
error: e,
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
console.error(`Error while applying changes to file ${fileChange.filePath}`, e);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return results;
|
|
391
|
+
}
|
|
392
|
+
exports.applyFileChanges = applyFileChanges;
|
package/dist/bin/index.js
CHANGED
package/dist/types/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|