-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapp.py
More file actions
259 lines (210 loc) Β· 9.93 KB
/
Copy pathapp.py
File metadata and controls
259 lines (210 loc) Β· 9.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
import streamlit as st
import subprocess
import requests
import sys
import os
import re
import difflib
# --- CONFIGURATION ---
MODEL_NAME = "qwen2.5-coder:1.5b"
OLLAMA_URL = "http://localhost:11434/api/generate"
LANGUAGE_CONFIG = {
".py": {
"name": "Python",
"command": [sys.executable],
"markdown_tag": "python"
},
".js": {
"name": "JavaScript",
"command": ["node"],
"markdown_tag": "javascript"
},
".java": {
"name": "Java",
"command": ["java"],
"markdown_tag": "java"
},
".cpp": {
"name": "C++",
"command": ["g++"],
"markdown_tag": "cpp"
},
".go": {
"name": "Go",
"command": ["go", "run"],
"markdown_tag": "go"
}
}
def get_language_config(file_path):
_, ext = os.path.splitext(file_path)
return LANGUAGE_CONFIG.get(ext)
# --- BACKEND LOGIC (Reused) ---
def run_code(file_path):
config = get_language_config(file_path)
if not config:
return 1, "", f"Unsupported file extension: {os.path.splitext(file_path)[1]}"
command = config["command"] + [file_path]
try:
# Note: Added shell=True for some environments where commands like 'node' or 'go'
# might not be found directly, though typically not needed for standard runtimes.
# Keeping timeout.
result = subprocess.run(
command,
capture_output=True,
text=True,
timeout=5
)
return result.returncode, result.stdout, result.stderr
except subprocess.TimeoutExpired:
return 1, "", "CRITICAL ERROR: Code Execution Timed Out (Possible Infinite Loop)"
except FileNotFoundError:
return 1, "", f"CRITICAL ERROR: Runtime not found for command: {command[0]}"
except Exception as e:
return 1, "", str(e)
# --- BACKEND LOGIC (MODIFIED: Refined prompt for logical reasoning) ---
def get_ai_fix(code_content, error_log, stdout_log, user_request, file_path):
config = get_language_config(file_path)
lang_name = config["name"] if config else "Unknown"
markdown_tag = config["markdown_tag"] if config else ""
# The prompt is simplified and places maximum emphasis on the User Request,
# treating STDOUT/STDERR as diagnostic feedback for the LLM.
prompt = f"""
You are an expert {lang_name} debugging agent specialized in **Code Reasoning**.
Your task is to fix both runtime errors and logical flaws based on the user's description.
### ULTIMATE GOAL (USER REQUEST):
"{user_request}"
### BROKEN CODE (Current Version):
```{markdown_tag}
{code_content}
```
### EXECUTION RESULT (Diagnostics):
- STDERR (Errors): {error_log if error_log else "None. The code ran without crashing."}
- STDOUT (Output): {stdout_log if stdout_log else "None"}
### INSTRUCTION:
1. Analyze the **ULTIMATE GOAL** and the **EXECUTION RESULT**.
2. Fix the code to address the issue described by the user, prioritizing functional correctness.
3. Return **ONLY** the fully fixed {lang_name} code inside a markdown block.
4. NO explanations, no added text outside the code block.
"""
payload = {
"model": MODEL_NAME,
"prompt": prompt,
"stream": False,
"options": {"temperature": 0.2}
}
try:
response = requests.post(OLLAMA_URL, json=payload)
response.raise_for_status()
result_text = response.json().get('response', '')
# Try to find code block with specific tag, fallback to generic or just text
pattern = r"```" + re.escape(markdown_tag) + r"(.*?)```"
code_match = re.search(pattern, result_text, re.DOTALL)
if not code_match:
code_match = re.search(r"```(.*?)```", result_text, re.DOTALL)
return code_match.group(1).strip() if code_match else result_text.strip()
except Exception as e:
return None
# --- STREAMLIT UI (MODIFIED: Removed Expected Output) ---
st.set_page_config(page_title="AutoDebug AI", page_icon="π€", layout="wide")
# Sidebar: File Selection
st.sidebar.title("π Workspace")
supported_extensions = tuple(LANGUAGE_CONFIG.keys())
files = [f for f in os.listdir('.') if f.endswith(supported_extensions) and f != 'app.py']
selected_file = st.sidebar.selectbox("Select a file to debug:", files)
# Main Chat Interface
st.title("π€ Local AI Debugger (Logic Mode)")
st.caption(f"Connected to **{MODEL_NAME}** | Editing: `{selected_file}`")
st.warning("β οΈ **Logical error fixing relies entirely on the quality of your description.**")
# Initialize Chat History
if "messages" not in st.session_state:
st.session_state.messages = [{"role": "assistant",
"content": "Hello! Select a file and clearly describe the bug (e.g., 'The loop runs infinitely' or 'The calculation is off by one')."}]
# Display Chat History
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
# Handle User Input
if user_input := st.chat_input("Describe the bug..."):
# 1. Show User Message
st.session_state.messages.append({"role": "user", "content": user_input})
with st.chat_message("user"):
st.markdown(user_input)
# 2. AI Processing
with st.chat_message("assistant"):
message_placeholder = st.empty()
if not selected_file:
message_placeholder.error("Please select a file from the sidebar first!")
else:
# --- FEATURE 2: AUTONOMOUS REPAIR LOOP (SIMPLIFIED LOGIC CHECK) ---
MAX_RETRIES = 5
# 1. Read Original Content (for diff & backup)
with open(selected_file, 'r') as f:
original_code = f.read()
current_code = original_code
# The success flag now only tracks if a non-crash state was reached AND
# if the model successfully provided a fix candidate. True success is assumed
# by the model having more context in the final step.
success = False
config = get_language_config(selected_file)
markdown_tag = config["markdown_tag"] if config else ""
with st.status("π Autonomous Debugging Loop (Logic/Runtime Fix)...", expanded=True) as status:
for iteration in range(1, MAX_RETRIES + 1):
status.write(f"**Attempt {iteration}/{MAX_RETRIES}:** Running Sandbox...")
# Write the current version to file to test it
with open(selected_file, 'w') as f:
f.write(current_code)
# Run the code
return_code, stdout, stderr = run_code(selected_file)
is_crash_error = (return_code != 0)
# Log the result for the user
if is_crash_error:
error_msg = stderr.strip().splitlines()[-1] if stderr else "Unknown Error"
status.write(f"Attempt {iteration} Result: β Runtime Error. Error: `{error_msg}`")
else:
status.write(
f"Attempt {iteration} Result: β
Code ran successfully. Now checking for logical fix...")
# Always consult the AI (unless it's the last try)
if iteration < MAX_RETRIES:
status.write(f"Consulting AI for fix #{iteration} based on user goal...")
# We pass the CURRENT code and the NEW execution results to the AI
fixed_code_candidate = get_ai_fix(current_code, stderr, stdout, user_input, selected_file)
if fixed_code_candidate:
# If the new code is identical to the old code, stop the loop early
if fixed_code_candidate.strip() == current_code.strip():
status.write(
f"Attempt {iteration}: AI returned identical code. Assuming no further fix can be determined.")
success = True
break
current_code = fixed_code_candidate
else:
status.error("AI failed to generate a fix.")
break
else:
# On the last iteration, we check the final state
if not is_crash_error:
success = True # Assume success if no crash on last attempt
break # Exit loop after last attempt
# --- FINAL RESULTS ---
if success:
# Create Backup of the ORIGINAL code
backup_path = f"{selected_file}.bak"
if not os.path.exists(backup_path):
with open(backup_path, 'w') as f:
f.write(original_code)
# Visual Diff (Original vs Final)
st.write("### βοΈ Code Comparison")
col1, col2 = st.columns(2)
with col1:
st.caption("β Original")
st.code(original_code, language=markdown_tag)
with col2:
st.caption(f"β
Fixed (Iter {iteration})")
st.code(current_code, language=markdown_tag)
# Persistence
response_msg = f"β
I have completed the repair process for `{selected_file}` after **{iteration} iterations**. The model attempted to fix the logical issue based on your request. A backup was saved to `{selected_file}.bak`."
st.session_state.messages.append({"role": "assistant", "content": response_msg})
else:
message_placeholder.error(
f"β Auto-repair failed after {MAX_RETRIES} attempts, or the code still has a critical error.")
with st.expander("View Final Error Log"):
st.code(stderr, language="bash")