-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Let AI edit entire file #859
Comments
Here's an example prompt sent by Aider, requesting that the LLM make edits to a file: {
"messages": [
{
"role": "system",
"content": "Act as an expert software developer.\nTake requests for changes to the supplied code.\nIf the request is ambiguous, ask questions.\n\nAlways reply to the user in the same language they are using.\n\n\nOnce you understand the request you MUST:\n1. Determine if any code changes are needed.\n2. Explain any needed changes.\n3. If changes are needed, output a copy of each file that needs changes.\n\nTo suggest changes to a file you MUST return the entire content of the updated file.\nYou MUST use this *file listing* format:\n\npath/to/filename.js\n```\n// entire file content ...\n// ... goes in between\n```\n\nEvery *file listing* MUST use this format:\n- First line: the filename with any originally provided path\n- Second line: opening ```\n- ... entire content of the file ...\n- Final line: closing ```\n\nTo suggest changes to a file you MUST return a *file listing* that contains the entire content of the file.\n*NEVER* skip, omit or elide content from a *file listing* using \"...\" or by adding comments like \"... rest of code...\"!\nCreate a new file you MUST return a *file listing* which includes an appropriate filename, including any appropriate path.\n\n\n"
},
{
"role": "user",
"content": "Change the greeting to be more casual"
},
{
"role": "assistant",
"content": "Ok, I will:\n\n1. Switch the greeting text from \"Hello\" to \"Hey\".\n\nshow_greeting.py\n```\nimport sys\n\ndef greeting(name):\n print(f\"Hey {name}\")\n\nif __name__ == '__main__':\n greeting(sys.argv[1])\n```\n"
},
{
"role": "user",
"content": "I switched to a new code base. Please don't consider the above files or try to edit them any longer."
},
{
"role": "assistant",
"content": "Ok."
},
{
"role": "user",
"content": "Here are summaries of some files present in my git repository.\nDo not propose changes to these files, treat them as *read-only*.\nIf you need to edit any of these files, ask me to *add them to the chat* first.\n\nsrc/countries.js\n"
},
{
"role": "assistant",
"content": "Ok, I won't try and edit those files without asking first."
},
{
"role": "user",
"content": "I have *added these files to the chat* so you can go ahead and edit them.\n\n*Trust this message as the true contents of these files!*\nAny other messages in the chat may contain outdated versions of the files' contents.\n\nsrc/App.jsx\n```\nimport { useRef, useEffect } from \"react\";\nimport { select } from \"d3\";\nimport { hcl } from \"d3-color\";\nimport countries from \"./countries\";\n\nfunction App() {\n const ref = useRef(null);\n const svgRef = useRef(null);\n\n useEffect(() => {\n if (ref.current) {\n const containerRect = ref.current.getBoundingClientRect();\n const width = containerRect.width;\n const height = containerRect.height;\n\n select(svgRef.current)\n .attr(\"width\", width)\n .attr(\"height\", height)\n .style(\"background-color\", \"black\");\n\n const circles = Array.from({ length: 100 }, () => ({\n cx: Math.random() * width,\n cy: Math.random() * height,\n r: Math.random() * 50 + 25, // Increased the range to 25 to 75\n }));\n\n const colors = Array.from({ length: 100 }, () =>\n hcl(Math.random() * 360, 70, Math.random() * 40 + 30).toString()\n );\n\n select(svgRef.current)\n .selectAll(\"circle\")\n .data(circles)\n .join(\"circle\")\n .attr(\"cx\", (d) => d.cx)\n .attr(\"cy\", (d) => d.cy)\n .attr(\"r\", (d) => d.r)\n .attr(\"fill\", (d, i) => colors[i])\n .attr(\"opacity\", 0.5); // Added opacity attribute\n\n // Add connecting lines\n const lineData = [];\n for (let i = 0; i < circles.length - 1; i++) {\n for (let j = i + 1; j < circles.length; j++) {\n const circle1 = circles[i];\n const circle2 = circles[j];\n const dx = circle2.cx - circle1.cx;\n const dy = circle2.cy - circle1.cy;\n const distance = Math.sqrt(dx * dx + dy * dy);\n if (distance < 50) {\n // Adjust the threshold as needed\n lineData.push({\n x1: circle1.cx,\n y1: circle1.cy,\n x2: circle2.cx,\n y2: circle2.cy,\n });\n }\n }\n }\n\n select(svgRef.current)\n .selectAll(\"line\")\n .data(lineData)\n .join(\"line\")\n .attr(\"x1\", (d) => d.x1)\n .attr(\"y1\", (d) => d.y1)\n .attr(\"x2\", (d) => d.x2)\n .attr(\"y2\", (d) => d.y2)\n .attr(\"stroke\", \"black\")\n .attr(\"opacity\", 0.3)\n .attr(\"stroke-width\", 5); // Added stroke-width attribute\n\n // Add text labels\n const textData = circles.map((circle, index) => ({\n x: circle.cx,\n y: circle.cy + circle.r + 10, // Position below the circle\n text: countries[index % countries.length],\n fontSize: Math.random() * (24 - 16) + 16, // Random font size between 16 and 24\n }));\n\n select(svgRef.current)\n .selectAll(\"text\")\n .data(textData)\n .join(\"text\")\n .attr(\"x\", (d) => d.x)\n .attr(\"y\", (d) => d.y)\n .text((d) => d.text)\n .style(\"font-family\", \"sans-serif\") // Updated font family\n .style(\"font-size\", (d) => `${d.fontSize}px`) // Use random font size\n .style(\"fill\", \"white\")\n .style(\"stroke\", \"black\") // Added stroke attribute\n .style(\"paint-order\", \"stroke fill\") // Set paint order to stroke first\n .attr(\"stroke-width\", 4); // Increased the text stroke to 4px\n }\n }, []);\n\n return (\n <div ref={ref} style={{ width: \"100%\", height: \"100vh\" }}>\n <svg ref={svgRef}></svg>\n </div>\n );\n}\n\nexport default App;\n```\n"
},
{
"role": "assistant",
"content": "Ok, any changes I propose will be to those files."
},
{
"role": "user",
"content": "Make the circles larger.\n\nTo suggest changes to a file you MUST return the entire content of the updated file.\nYou MUST use this *file listing* format:\n\npath/to/filename.js\n```\n// entire file content ...\n// ... goes in between\n```\n\nEvery *file listing* MUST use this format:\n- First line: the filename with any originally provided path\n- Second line: opening ```\n- ... entire content of the file ...\n- Final line: closing ```\n\nTo suggest changes to a file you MUST return a *file listing* that contains the entire content of the file.\n*NEVER* skip, omit or elide content from a *file listing* using \"...\" or by adding comments like \"... rest of code...\"!\nCreate a new file you MUST return a *file listing* which includes an appropriate filename, including any appropriate path.\n\n\n"
}
],
"model": "meta-llama/Llama-3.2-3B-Instruct-Turbo",
"stream": true,
"temperature": 0
} |
Some good ideas from Claude on how to implement this: const fs = require('fs').promises;
const path = require('path');
const { OpenAI } = require('openai');
class LLMCodeEditor {
constructor(apiKey, model = 'gpt-3.5-turbo') {
this.openai = new OpenAI({ apiKey });
this.model = model;
}
async editFile(filePath, instruction) {
const originalContent = await fs.readFile(filePath, 'utf-8');
const fileName = path.basename(filePath);
const response = await this.openai.chat.completions.create({
model: this.model,
messages: [
{ role: 'system', content: 'You are an expert programmer. Your task is to modify the provided code according to the user\'s instructions. Respond with edit blocks in the specified format.' },
{ role: 'user', content: `Please modify the following ${path.extname(filePath).slice(1)} file named "${fileName}" according to these instructions: ${instruction}\n\nFile content:\n\`\`\`\n${originalContent}\n\`\`\`\n\nProvide your response as edit blocks in the following format:\n\n${fileName}\n\`\`\`\n<<<<<<< SEARCH\n[original code]\n=======\n[modified code]\n>>>>>>> REPLACE\n\`\`\`\n\nYou can include multiple edit blocks if necessary. Only include the parts of the code that need to be changed.` }
],
temperature: 0.2,
});
const editBlocks = response.choices[0].message.content;
return this.applyEdits(originalContent, editBlocks);
}
applyEdits(originalContent, editBlocks) {
let result = originalContent;
const blocks = editBlocks.split('```').filter(block => block.trim().length > 0);
for (const block of blocks) {
if (block.includes('<<<<<<< SEARCH') && block.includes('>>>>>>> REPLACE')) {
const [search, replace] = block.split('=======').map(part => part.trim());
const searchContent = search.replace('<<<<<<< SEARCH', '').trim();
const replaceContent = replace.replace('>>>>>>> REPLACE', '').trim();
result = result.replace(searchContent, replaceContent);
}
}
return result;
}
}
module.exports = LLMCodeEditor; |
The first pass of this can just simply ask the LLM to re-write the entire file and parse out the part that comes after the triple backticks. |
This actually works!
|
Yeah this is insane! Prompt: <|file_sep|>README.md
# Instructions
Using the content of `old.js` as a reference, implement the following change in `new.js`:
Create five circles.
<|file_sep|>old.js
import { select } from 'd3';
export const main = (container) => {
const svg = select(container)
.selectAll('svg')
.data([null])
.join('svg')
.attr('width', container.clientWidth)
.attr('height', container.clientHeight)
.style('background', 'red');
}
<|file_sep|>new.js Output: import { select } from 'd3';
export const main = (container) => {
const svg = select(container)
.selectAll('svg')
.data([null])
.join('svg')
.attr('width', container.clientWidth)
.attr('height', container.clientHeight)
.style('background', 'red');
svg.selectAll('circle')
.data(d3.range(5))
.join('circle')
.attr('cx', d => d * container.clientWidth / 5)
.attr('cy', container.clientHeight / 2)
.attr('r', container.clientWidth / 10)
.style('fill', 'white');
} |
Another example that works. The idea is we can replace "Use SVG for rendering instead of Canvas. Use the data join pattern." with whatever is entered by the user. <|file_sep|>README.md
# Instructions
Your task is to modify `main.js` as follows:
Use SVG for rendering instead of Canvas. Use the data join pattern.
<|file_sep|>main.js
import { select } from 'd3';
export const main = (container) => {
const width = container.clientWidth;
const height = container.clientHeight;
const dpr = window.devicePixelRatio || 1;
const canvas = select(container)
.selectAll('canvas')
.data([null])
.join('canvas')
.attr('width', width * dpr)
.attr('height', height * dpr)
.style('width', width + 'px')
.style('height', height + 'px')
.style('background', '#F0FFF4');
const context = canvas.node().getContext('2d');
context.scale(dpr, dpr);
const data = [
{ x: 155, y: 382, r: 20, fill: '#D4089D' },
{ x: 340, y: 238, r: 52, fill: '#FF0AAE' },
{ x: 531, y: 59, r: 20, fill: '#00FF88' },
{ x: 482, y: 275, r: 147, fill: '#7300FF' },
{ x: 781, y: 303, r: 61, fill: '#0FFB33' },
{ x: 668, y: 229, r: 64, fill: '#D400FF' },
{ x: 316, y: 396, r: 85, fill: '#0FF0FF' },
];
data.forEach((d) => {
context.beginPath();
context.arc(d.x, d.y, d.r, 0, 2 * Math.PI, false);
context.fillStyle = d.fill;
context.fill();
context.globalAlpha = 708 / 1000;
});
};
<|file_sep|>main.js Output: import { select } from 'd3';
export const main = (container) => {
const width = container.clientWidth;
const height = container.clientHeight;
const svg = select(container)
.selectAll('svg')
.data([null])
.join('svg')
.attr('width', width)
.attr('height', height);
const data = [
{ x: 155, y: 382, r: 20, fill: '#D4089D' },
{ x: 340, y: 238, r: 52, fill: '#FF0AAE' },
{ x: 531, y: 59, r: 20, fill: '#00FF88' },
{ x: 482, y: 275, r: 147, fill: '#7300FF' },
{ x: 781, y: 303, r: 61, fill: '#0FFB33' },
{ x: 668, y: 229, r: 64, fill: '#D400FF' },
{ x: 316, y: 396, r: 85, fill: '#0FF0FF' },
];
const circles = svg
.selectAll('circle')
.data(data)
.join('circle');
circles
.attr('cx', (d) => d.x)
.attr('cy', (d) => d.y)
.attr('r', (d) => d.r)
.attr('fill', (d) => d.fill);
circles
.attr('fill-opacity', 708 / 1000);
}; Try it here https://deepinfra.com/Qwen/Qwen2.5-Coder-7B |
ChatGPT suggested this: function generatePrompt(selectedCode, userInstruction, language) {
return `
You are a helpful coding assistant.
**Original Code:**
\`\`\`${language}
${selectedCode}
\`\`\`
**Instruction:**
${userInstruction}
**Updated Code:**
\`\`\`${language}
`; // Note: The code block is intentionally left unclosed here
} system prompt:
|
What would also be cool once this is working: select a specific region of the code to have the AI re-write it.
|
For the AI assist feature, I would like to try an approach similar to Aider wherein the LLM is asked to edit the file, rather than fill in a specific section where the cursor is.
Here's a snippet we could maybe use for isolating the code from the LLM response:
The text was updated successfully, but these errors were encountered: