Add automatic code error fixing feature
Implements an AI-powered auto-fix system that automatically attempts to repair code execution errors up to 3 times. Backend changes (app.py): - Add /api/fix-code endpoint that uses MODEL_1 (Hermes) to analyze stderr output and generate corrected code - System prompt instructs AI to output only raw fixed code without explanations or markdown formatting - Accepts code, language, stderr, exit_code, and attempt number Frontend changes (templates/chat.html): - Modify executeCodeBlock() to detect failed executions (exit_code !== 0) - Track fix attempts per code block (max 3) using dataset attributes - Call /api/fix-code when errors are detected - Display fixed code as new chat message with markdown formatting - Automatically re-execute the corrected code recursively - Show progress messages during auto-fix attempts - Display warning when max attempts (3) are exhausted Features: - Fixes common issues: missing imports, syntax errors, type errors - Posts fixed code to chat for transparency - Prevents infinite loops with 3-attempt limit - Graceful error handling with user-friendly status messages
This commit is contained in:
parent
a3d1e18a37
commit
6400a5eacb
2 changed files with 169 additions and 0 deletions
82
app.py
82
app.py
|
|
@ -850,6 +850,88 @@ Examples:
|
|||
return jsonify({"filename": "compiled_binary"})
|
||||
|
||||
|
||||
@app.route("/api/fix-code", methods=["POST"])
|
||||
def fix_code():
|
||||
"""Auto-fix code errors by asking AI to fix issues based on stderr output.
|
||||
|
||||
Accepts code, language, stderr, and attempt number.
|
||||
Returns fixed code block or error message.
|
||||
"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
code = data.get("code", "")
|
||||
language = data.get("language", "")
|
||||
stderr = data.get("stderr", "")
|
||||
exit_code = data.get("exit_code", 1)
|
||||
attempt = data.get("attempt", 1)
|
||||
|
||||
if not code or not language:
|
||||
return jsonify({"error": "Code and language are required"}), 400
|
||||
|
||||
# Use MODEL_1 (Hermes) to fix the code
|
||||
client, model = get_openai_client_and_model("MODEL_1")
|
||||
|
||||
system_prompt = f"""You are an expert {language} programmer and debugger. Your task is to fix code that has errors.
|
||||
|
||||
CRITICAL RULES:
|
||||
- Output ONLY the fixed code, nothing else
|
||||
- NO explanations, NO comments about what you changed
|
||||
- NO markdown code fences (```), just the raw code
|
||||
- Preserve the original code structure and logic as much as possible
|
||||
- Fix ONLY the errors reported in stderr
|
||||
- If the error mentions missing imports/includes, add them at the top
|
||||
- If the error is a syntax error, fix the syntax
|
||||
- Keep the same variable names and overall approach
|
||||
|
||||
The code should be immediately executable without any modifications."""
|
||||
|
||||
user_prompt = f"""The following {language} code has errors:
|
||||
|
||||
```{language}
|
||||
{code}
|
||||
```
|
||||
|
||||
Error output (exit code {exit_code}):
|
||||
```
|
||||
{stderr}
|
||||
```
|
||||
|
||||
Fix the code (output ONLY the corrected code, no explanations):"""
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model=model,
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt}
|
||||
],
|
||||
temperature=0.2, # Low temperature for consistent fixes
|
||||
max_tokens=2000
|
||||
)
|
||||
|
||||
fixed_code = response.choices[0].message.content.strip()
|
||||
|
||||
# Clean up any markdown code fences that might have slipped through
|
||||
if fixed_code.startswith("```"):
|
||||
lines = fixed_code.split("\n")
|
||||
# Remove first line if it's a fence
|
||||
if lines[0].startswith("```"):
|
||||
lines = lines[1:]
|
||||
# Remove last line if it's a fence
|
||||
if lines and lines[-1].strip() == "```":
|
||||
lines = lines[:-1]
|
||||
fixed_code = "\n".join(lines)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"fixed_code": fixed_code,
|
||||
"attempt": attempt
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error fixing code: {e}")
|
||||
return jsonify({"error": f"Failed to fix code: {str(e)}"}), 500
|
||||
|
||||
|
||||
@app.route("/chat/<room_name>")
|
||||
def chat(room_name):
|
||||
user = auth.get_current_user()
|
||||
|
|
|
|||
|
|
@ -1552,6 +1552,93 @@ async function executeCodeBlock(code, blockElement, playButton) {
|
|||
if (job.status === 'completed') {
|
||||
// Unsandbox returns stdout/stderr/exit_code at top level of job response
|
||||
displayExecutionResults(job, resultsContainer, language, code);
|
||||
|
||||
// Check if execution failed and attempt auto-fix
|
||||
const exitCode = job.exit_code;
|
||||
const stderr = job.stderr || '';
|
||||
const shouldAutoFix = exitCode !== 0 && stderr.trim() !== '';
|
||||
|
||||
// Track attempts per code block (store in resultsContainer dataset)
|
||||
if (!resultsContainer.dataset.fixAttempts) {
|
||||
resultsContainer.dataset.fixAttempts = '0';
|
||||
}
|
||||
|
||||
const currentAttempts = parseInt(resultsContainer.dataset.fixAttempts);
|
||||
|
||||
if (shouldAutoFix && currentAttempts < 3) {
|
||||
// Show auto-fix message
|
||||
const autoFixDiv = document.createElement('div');
|
||||
autoFixDiv.style.color = 'var(--text-info)';
|
||||
autoFixDiv.style.fontWeight = 'bold';
|
||||
autoFixDiv.style.marginTop = '12px';
|
||||
autoFixDiv.textContent = `Attempting to auto-fix errors (attempt ${currentAttempts + 1}/3)...`;
|
||||
resultsContainer.appendChild(autoFixDiv);
|
||||
|
||||
// Increment attempt counter
|
||||
resultsContainer.dataset.fixAttempts = (currentAttempts + 1).toString();
|
||||
|
||||
// Call backend to fix the code
|
||||
try {
|
||||
const fixResponse = await fetch('/api/fix-code', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
code: code,
|
||||
language: language,
|
||||
stderr: stderr,
|
||||
exit_code: exitCode,
|
||||
attempt: currentAttempts + 1
|
||||
})
|
||||
});
|
||||
|
||||
if (fixResponse.ok) {
|
||||
const fixData = await fixResponse.json();
|
||||
|
||||
if (fixData.success && fixData.fixed_code) {
|
||||
// Update status
|
||||
autoFixDiv.textContent = `Code fixed! Re-executing (attempt ${currentAttempts + 1}/3)...`;
|
||||
|
||||
// Post the fixed code as a new message in the chat
|
||||
socket.emit("chat_message", {
|
||||
"username": username,
|
||||
"message": `**Auto-fixed code (attempt ${currentAttempts + 1}/3):**\n\n\`\`\`${language}\n${fixData.fixed_code}\n\`\`\``,
|
||||
"model": "None",
|
||||
"room_name": room_name
|
||||
});
|
||||
|
||||
// Wait a moment for the message to be posted
|
||||
await sleep(500);
|
||||
|
||||
// Re-execute with the fixed code
|
||||
// Create a small delay to prevent stack overflow
|
||||
await sleep(200);
|
||||
await executeCodeBlock(fixData.fixed_code, blockElement, playButton);
|
||||
return; // Exit this execution, the recursive call will handle the rest
|
||||
} else {
|
||||
autoFixDiv.textContent = `Auto-fix failed: ${fixData.error || 'Unknown error'}`;
|
||||
autoFixDiv.style.color = 'var(--text-error)';
|
||||
}
|
||||
} else {
|
||||
autoFixDiv.textContent = `Auto-fix request failed (HTTP ${fixResponse.status})`;
|
||||
autoFixDiv.style.color = 'var(--text-error)';
|
||||
}
|
||||
} catch (autoFixError) {
|
||||
console.error('Error during auto-fix:', autoFixError);
|
||||
autoFixDiv.textContent = `Auto-fix error: ${autoFixError.message}`;
|
||||
autoFixDiv.style.color = 'var(--text-error)';
|
||||
}
|
||||
} else if (currentAttempts >= 3 && shouldAutoFix) {
|
||||
// Max attempts reached
|
||||
const maxAttemptsDiv = document.createElement('div');
|
||||
maxAttemptsDiv.style.color = 'var(--text-warning)';
|
||||
maxAttemptsDiv.style.fontWeight = 'bold';
|
||||
maxAttemptsDiv.style.marginTop = '12px';
|
||||
maxAttemptsDiv.textContent = 'Maximum auto-fix attempts (3) reached. Code still has errors.';
|
||||
resultsContainer.appendChild(maxAttemptsDiv);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue