Skip to content

Commit

Permalink
add read operation
Browse files Browse the repository at this point in the history
  • Loading branch information
ZHallen122 committed Feb 8, 2025
1 parent 0bfcf37 commit 0be6af5
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Logger } from '@nestjs/common';
import { writeFile, rename } from 'fs/promises';
import { writeFile, rename, readFile } from 'fs/promises';
import path from 'path';

export interface FileOperation {
action: 'write' | 'rename';
action: 'write' | 'rename' | 'read';
originalPath?: string;
renamePath?: string;
code?: string;
Expand Down Expand Up @@ -37,6 +37,10 @@ export class FileOperationManager {
await this.handleRename(op);
newFilePath = op.renamePath || null;
break;
case 'read':
await this.handleRead(op);
newFilePath = op.renamePath || null;
break;
}
} catch (error) {
this.logger.error(
Expand All @@ -57,6 +61,27 @@ export class FileOperationManager {
await writeFile(originalPath, op.code, 'utf-8');
}

private async handleRead(op: FileOperation): Promise<string | null> {
try {
const originalPath = path.resolve(this.projectRoot, op.originalPath);
this.safetyChecks(originalPath);

this.logger.debug(`Reading file: ${originalPath}`);

// Read the file content
const fileContent = await readFile(originalPath, 'utf-8');

this.logger.debug(`File read successfully: ${originalPath}`);

return fileContent;
} catch (error) {
this.logger.error(
`Failed to read file: ${op.originalPath}, Error: ${error.message}`,
);
return null; // Return null if the file cannot be read
}
}

private async handleRename(op: FileOperation): Promise<void> {
const originalPath = path.resolve(this.projectRoot, op.originalPath);
const RenamePath = path.resolve(this.projectRoot, op.renamePath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export class FixResponseParser {
renamePath: op.path,
code: parsedData.fix.generate?.trim(),
};
} else if (op.type === 'READ') {
return {
action: 'read',
originalPath: op.original_path,
};
}
return null;
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class FrontendQueueProcessor {
// 1. Write the file to disk
createFileWithRetries(currentFullFilePath, task.fileContents);

const maxFixAttempts = 3;
const maxFixAttempts = 2;

for (let attempt = 1; attempt <= maxFixAttempts; attempt++) {
const validationResult = await this.validator.validate();
Expand Down Expand Up @@ -143,7 +143,7 @@ export class FrontendQueueProcessor {
//this.logger.log(fixPrompt);

// Use model for a fix
const fixResponse = await chatSyncWithClocker(
let fixResponse = await chatSyncWithClocker(
this.context,
{
model: 'gpt-4o',
Expand Down Expand Up @@ -179,7 +179,76 @@ export class FrontendQueueProcessor {
this.logger.debug('dependency file Paths ' + task.dependenciesPath);
const parsed_fixResponse = removeCodeBlockFences(fixResponse);

const operations = parser.parse(parsed_fixResponse, task.filePath);
let operations = parser.parse(parsed_fixResponse, task.filePath);

// **If LLM requested additional files, read them**
if (operations.some((op) => op.action === 'read')) {
this.logger.log(
`LLM requested additional context. Reading dependencies...`,
);

for (const op of operations) {
if (op.action === 'read' && op.originalPath) {
try {
op.code = readFileSync(
path.resolve(this.frontendPath, op.originalPath),
'utf-8',
);
this.logger.log(`Read file: ${op.originalPath}`);
} catch (error) {
this.logger.warn(
`Failed to read file: ${op.originalPath}. Error: ${error.message}`,
);
}
}
}

// **Second Attempt: Retry fix with additional file content**
fixResponse = await chatSyncWithClocker(
this.context,
{
model: 'gpt-4o',
messages: [
{ role: 'system', content: fixPrompt },
{
role: 'user',
content: `Current file path that needs fixing: \n ${task.filePath}`,
},
{ role: 'user', content: `Error messages: \n ${rawErrorText}` },
{
role: 'user',
content: ` dependency file Paths: \n ${task.dependenciesPath}`,
},
{
role: 'user',
content: `Original content:\n ${originalContent}`,
},
{
role: 'user',
content: `Additional imported files:\n ${operations
.filter((op) => op.action === 'read' && op.code)
.map((op) => `File: ${op.originalPath}\nContent:\n${op.code}`)
.join('\n\n')}`,
},
{
role: 'assistant',
content: `Do this really fix the provide code?
This time I shouldn't use the read tool because previous context already use it.
Let me check some common issue to make sure my answer is correct ${commonIssuePrompt}. If not I should modify the result.
If i am using rename tool am i use the correct Current file path for it?
I must follow the output format.`,
},
],
},
'fix code (generic)',
'FrontendQueueProcessor',
);
this.logger.debug(
'Updated Fix Response with extra context: ' + fixResponse,
);
const updated_fixResponse = removeCodeBlockFences(fixResponse);
operations = await parser.parse(updated_fixResponse, task.filePath);
}

const newFilePath =
await fileOperationManager.executeOperations(operations);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export function generateFileOperationPrompt(): string {
Available Tools:
1. WRITE - Modify/create files
2. RENAME - RENAME the file
3. READ - Read the file
**Instructions:**
1. **Understand the error from \`error\`** and determine what's wrong.
Expand All @@ -173,13 +174,17 @@ Available Tools:
8. File Operations:
- Use RENAME only for extension issues (e.g., .js → .tsx)
- Use WRITE for code/content changes
- Use READ for not enough information so solve the issue
9.generate must include full code.
9. When User provide "Additional imported files" use this as to help you fix the error. Remeber only write other files code to current file when it is necessary
10. generate must include full code.
**Common Errors & Fixes**
JSX file naming issue → Use the RENAME tool.
TypeScript type error → Use the WRITE tool.
Not assignable to parameter of type → Use the READ tool if you dont have enough information to fix.
**safety check**
1. Never delete files outside /src directory
Expand All @@ -200,6 +205,10 @@ Respond format in this json format:
"path": "src/new/path/here.tsx",
"original_path": "src/old/path/here.ts"
}
{
"type": "READ",
"original_path": "src/the/path/read.tsx"
}
],
"generate": " Code here "
}
Expand Down Expand Up @@ -234,6 +243,20 @@ Example Good RENAME Operation:
}
}
Example Good READ Operation:
{
"fix": {
"operations": [
{
"type": "READ",
"original_path": "src/utils/helpers.tsx",
}
... You can ask to read more then one file.
],
"generate": ""
}
}
**Important Note**
1.The output must be complete and strictly formatted.
2.DO NOT EXPLAIN OUTSIDE JSON.
Expand Down Expand Up @@ -263,5 +286,35 @@ Fix:
</OPERATIONS>
<GENERATE></GENERATE>
</FIX>
2. defined but never used Issue -> Use the WRITE Tool
Error example:
'useEffect' is defined but never used.
Fix:
Remove the defined but never used component from the import.
✅ Correct Fix Output:
{
"fix": {
"operations": [
{
"type": "WRITE"
}
],
"generate": "Do the fix for the import and other part stay the same and put Full code here!"
}
}
3. Import error -> Use the WRITE Tool
Error example:
Cannot find module './components/GlobalFooter' or its corresponding type declarations.
Fix:
Read carefully about previous "dependency file Paths" use the path in the fix if applicable.
`;
}

0 comments on commit 0be6af5

Please sign in to comment.