suprimedev commited on
Commit
8256b65
·
verified ·
1 Parent(s): 5074e1b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +368 -165
app.py CHANGED
@@ -11,6 +11,8 @@ from contextlib import redirect_stdout
11
  import textwrap
12
  import shutil
13
  import traceback
 
 
14
 
15
  # Clean up any existing temp files on startup to save space
16
  try:
@@ -44,6 +46,89 @@ client = openai.OpenAI(
44
 
45
  MODEL_NAME = "x-ai/grok-4-fast:free"
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  def indent_code(code, spaces=4):
48
  """Indent the code by the specified number of spaces."""
49
  indented_lines = []
@@ -78,20 +163,20 @@ def detect_required_packages(code):
78
 
79
  # Usage patterns that require additional packages
80
  usage_patterns = {
81
- r'\.to_excel': 'openpyxl', # pandas.to_excel needs openpyxl
82
- r'\.read_excel': 'openpyxl', # pandas.read_excel needs openpyxl
83
- r'\.ExcelWriter': 'openpyxl', # pandas ExcelWriter
84
- r'\.to_parquet': 'pyarrow', # pandas parquet support
85
  r'\.read_parquet': 'pyarrow',
86
- r'\.to_sql': 'sqlalchemy', # pandas SQL support
87
  r'\.read_sql': 'sqlalchemy',
88
- r'\.to_feather': 'pyarrow', # pandas feather format
89
  r'\.read_feather': 'pyarrow',
90
- r'\.to_stata': 'statsmodels', # pandas stata support
91
  r'\.read_stata': 'statsmodels',
92
- r'\.to_clipboard': 'pyperclip', # pandas clipboard
93
- r'xlsxwriter': 'xlsxwriter', # Excel writer engine
94
- r'openpyxl': 'openpyxl', # Excel engine
95
  }
96
 
97
  # Check for pip install comments
@@ -164,13 +249,11 @@ def detect_required_packages(code):
164
  for pattern, package in usage_patterns.items():
165
  if re.search(pattern, code, re.IGNORECASE) and package not in pre_installed:
166
  required_packages.add(package)
167
- print(f"Detected usage pattern '{pattern}' requiring {package}")
168
 
169
  # Special case: if pandas is imported and Excel operations detected
170
  if 'pandas' in code and any(pattern in code for pattern in ['.to_excel', '.read_excel', 'ExcelWriter']):
171
  if 'openpyxl' not in pre_installed:
172
  required_packages.add('openpyxl')
173
- print("Detected pandas Excel operations, adding openpyxl")
174
 
175
  # Remove pre-installed packages
176
  required_packages = required_packages - pre_installed
@@ -198,7 +281,6 @@ def install_package(package_name):
198
  spec = importlib.util.find_spec(import_name)
199
  if spec is None:
200
  print(f"Installing package: {package_name}")
201
- # Remove --no-deps to ensure dependencies are installed
202
  result = subprocess.run([
203
  sys.executable, "-m", "pip", "install", "--quiet", "--no-cache-dir", package_name
204
  ], capture_output=True, text=True)
@@ -239,67 +321,82 @@ def install_packages_if_needed(packages):
239
 
240
  return len(failed_packages) == 0
241
 
242
- def generate_code_with_openrouter(instruction, file_paths):
243
- """Generate Python code using OpenRouter API."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  prompt_template = textwrap.dedent("""
245
  You are a Python expert. Instruction: "{instruction}"
246
  Input files: {file_paths_str} (use file_paths[0] for first file, iterate for multiple; if empty, generate based on instruction alone).
 
 
247
 
248
  Write a complete Python script that:
249
  1. Import all necessary libraries at the top.
250
- 2. Add "# pip install package_name" comments after imports for all needed libraries with version.
251
  3. Define file_paths = {file_paths_list}
252
- 4. Perform the requested operation.
253
- 5. Save output to a temp directory using tempfile.mkdtemp()
254
- 6. Print "OUTPUT_FILE_PATH: /full/path/to/output" at the end using os.path.abspath()
 
 
255
 
256
  Important rules:
257
  - For pandas Excel operations, always add: # pip install openpyxl
258
- - For image operations, add relevant pip install comments
259
- - For ML/AI operations, ensure proper imports with pip comments
260
- - If no files provided, generate data as per instruction
261
- - Always use try-except for error handling
262
  - No __name__ == '__main__', no functions, just direct code
 
263
 
264
- Examples:
265
-
266
- For Excel generation:
267
- import os
268
- import tempfile
269
- import pandas as pd
270
- # pip install openpyxl
271
- import random
272
-
273
- file_paths = {file_paths_list}
274
- temp_dir = tempfile.mkdtemp()
275
- output_path = os.path.join(temp_dir, 'data.xlsx')
276
-
277
- # Generate data
278
- data = []
279
- for i in range(1000):
280
- data.append({{'Name': f'Person {{i}}', 'Age': random.randint(20, 60)}})
281
-
282
- df = pd.DataFrame(data)
283
- df.to_excel(output_path, index=False, engine='openpyxl')
284
-
285
- print(f"OUTPUT_FILE_PATH: {{os.path.abspath(output_path)}}")
286
-
287
- For image processing:
288
  import os
289
  import tempfile
290
- from PIL import Image
291
- # pip install Pillow
 
 
 
 
292
 
293
  file_paths = {file_paths_list}
294
- input_path = file_paths[0]
295
- temp_dir = tempfile.mkdtemp()
296
- output_path = os.path.join(temp_dir, 'processed.png')
297
 
298
- img = Image.open(input_path)
299
- # Process image...
300
- img.save(output_path)
301
-
302
- print(f"OUTPUT_FILE_PATH: {{os.path.abspath(output_path)}}")
 
 
 
 
 
 
 
 
 
 
 
303
 
304
  Output ONLY Python code, no markdown.
305
  """)
@@ -310,18 +407,20 @@ Output ONLY Python code, no markdown.
310
  prompt = prompt_template.format(
311
  instruction=instruction or "No instruction provided",
312
  file_paths_str=file_paths_str,
313
- file_paths_list=file_paths_list
 
 
314
  )
315
 
316
  try:
317
  response = client.chat.completions.create(
318
  model=MODEL_NAME,
319
  messages=[
320
- {"role": "system", "content": "Output only clean executable Python code with necessary pip install comments."},
321
  {"role": "user", "content": prompt}
322
  ],
323
  max_tokens=4000,
324
- temperature=0.1
325
  )
326
 
327
  generated_code = response.choices[0].message.content.strip()
@@ -339,130 +438,234 @@ Output ONLY Python code, no markdown.
339
  print("OUTPUT_TEXT: Code generation failed due to API error")
340
  sys.exit(0)"""
341
 
342
- def process_request(instruction, files):
 
343
  tf_path = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  try:
345
  if not instruction.strip():
346
  return "لطفاً دستور را وارد کنید. (فایل‌ها اختیاری هستند)", None
347
 
348
  file_paths = [f.name for f in files] if files else []
349
 
350
- # Step 1: Generate code
351
- print("Generating code...")
352
- generated_code = generate_code_with_openrouter(instruction, file_paths)
353
 
354
- if len(generated_code) < 20:
355
- return f"کد ضعیف تولید شد: {generated_code}", None
356
-
357
- print("Generated code preview:", generated_code[:200])
358
-
359
- # Step 2: Detect and install packages
360
- print("Detecting packages...")
361
- required_packages = detect_required_packages(generated_code)
362
- print("Detected packages:", required_packages)
363
- all_installed = install_packages_if_needed(required_packages)
364
-
365
- if not all_installed:
366
- print("Warning: Some packages failed to install, but continuing...")
367
-
368
- # Step 3: Wrap code
369
- indented = indent_code(generated_code)
370
- wrapped_code = f"try:\n{indented}\nexcept Exception as e:\n print(f'ERROR: {{e}}')\n import traceback; traceback.print_exc()\n import sys; sys.exit(1)"
371
-
372
- # Step 4: Compile check
373
- print("Compiling code...")
374
- try:
375
- compile(wrapped_code, '<string>', 'exec')
376
- print("Compile OK.")
377
- except SyntaxError as se:
378
- error_msg = f"Syntax Error: {se}\nCode preview: {wrapped_code[:500]}...\nFull traceback: {traceback.format_exc()}"
379
- return error_msg, None
380
-
381
- # Step 5: Temp file
382
- print("Creating temp file...")
383
- with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as tf:
384
- tf.write(wrapped_code)
385
- tf_path = tf.name
386
- print(f"Temp file: {tf_path}")
387
-
388
- # Step 6: Execute with retries
389
- max_retries = 2
390
- for retry in range(max_retries + 1):
391
- print(f"Executing (attempt {retry + 1})...")
392
- try:
393
- result = subprocess.run(
394
- [sys.executable, tf_path],
395
- capture_output=True,
396
- text=True,
397
- timeout=60 # Increased timeout
398
- )
399
- stdout, stderr = result.stdout, result.stderr
400
- rc = result.returncode
401
-
402
- if rc != 0:
403
- # Check for ImportError and retry
404
- import_error_match = re.search(r"No module named ['\"]([^'\"]+)['\"]", stderr)
405
- if import_error_match and retry < max_retries:
406
- missing_module = import_error_match.group(1)
407
- print(f"Detected missing module: {missing_module}")
408
- if install_package(missing_module):
409
- print(f"Installed {missing_module}, retrying...")
410
- continue
411
-
412
- error_msg = f"Execution failed (RC {rc} after {retry + 1} tries):\nStdout: {stdout}\nStderr: {stderr}\nCode preview: {wrapped_code[:500]}..."
413
- return error_msg, None
414
-
415
- # Extract output (file or text)
416
- output_path_match = re.search(r'OUTPUT_FILE_PATH:\s*(.+)', stdout, re.I)
417
- output_text_match = re.search(r'OUTPUT_TEXT:\s*(.+)', stdout, re.I | re.DOTALL)
418
 
419
- if output_path_match:
420
- output_path = output_path_match.group(1).strip()
421
- if os.path.exists(output_path):
422
- return f"Success!\nGenerated Code:\n{generated_code}\n\nStdout:\n{stdout}", output_path
423
- else:
424
- return f"Output path not found: {output_path}\n{stdout}", None
425
- elif output_text_match:
426
- return f"Success (text output)!\nGenerated Code:\n{generated_code}\n\nOutput:\n{output_text_match.group(1).strip()}", None
427
- else:
428
- # Check if there's any output at all
429
- if stdout.strip():
430
- return f"Execution completed but no OUTPUT marker found:\n{stdout}\n\nGenerated code:\n{generated_code}", None
431
- else:
432
- return f"No output generated:\nStderr: {stderr}\nCode: {wrapped_code}", None
433
-
434
- except subprocess.TimeoutExpired:
435
- return f"Timeout (60s): Code execution took too long.", None
436
- except Exception as sub_error:
437
- if retry < max_retries:
438
- print(f"Subprocess error on retry {retry + 1}: {sub_error}")
439
- continue
440
- error_msg = f"Subprocess error after {max_retries + 1} tries: {sub_error}\n{traceback.format_exc()}"
441
- return error_msg, None
442
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
  except Exception as e:
444
  error_msg = f"General error: {type(e).__name__}: {e}\nFull traceback: {traceback.format_exc()}"
445
  print(error_msg)
446
  return error_msg, None
447
- finally:
448
- if tf_path and os.path.exists(tf_path):
449
- try:
450
- os.unlink(tf_path)
451
- print("Cleaned up temp file.")
452
- except:
453
- pass
454
 
455
  # Gradio Interface
456
- with gr.Blocks(title="AI File Processor") as demo:
457
- gr.Markdown("# AI File Processor\nGive an instruction (e.g., 'generate sample data to Excel' or 'remove background from image'). Files are optional.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
458
 
459
  with gr.Row():
460
- instruction = gr.Textbox(label="Instruction", lines=2, placeholder="e.g., Generate 1000 Iranian names and mobiles to Excel")
461
- files = gr.File(file_count="multiple", label="Files (optional)")
462
 
463
- btn = gr.Button("Process")
464
- output = gr.Textbox(label="Result", lines=10)
465
- file_out = gr.File(label="Output File (if generated)")
 
 
 
 
 
 
466
 
467
  btn.click(fn=process_request, inputs=[instruction, files], outputs=[output, file_out])
468
 
 
11
  import textwrap
12
  import shutil
13
  import traceback
14
+ import json
15
+ from typing import List, Tuple, Optional, Dict
16
 
17
  # Clean up any existing temp files on startup to save space
18
  try:
 
46
 
47
  MODEL_NAME = "x-ai/grok-4-fast:free"
48
 
49
+ class ErrorAnalyzer:
50
+ """Analyze errors and suggest fixes"""
51
+
52
+ @staticmethod
53
+ def analyze_error(error_message: str, code: str) -> Dict:
54
+ """Analyze error and return fix strategy"""
55
+ error_type = "unknown"
56
+ suggestions = []
57
+ packages_to_install = []
58
+
59
+ # Import errors
60
+ if "No module named" in error_message or "ModuleNotFoundError" in error_message:
61
+ error_type = "import_error"
62
+ module_match = re.search(r"No module named ['\"]([^'\"]+)['\"]", error_message)
63
+ if module_match:
64
+ module = module_match.group(1)
65
+ packages_to_install.append(module)
66
+ suggestions.append(f"Install missing module: {module}")
67
+
68
+ # Permission errors
69
+ elif "Permission denied" in error_message or "PermissionError" in error_message:
70
+ error_type = "permission_error"
71
+ suggestions.append("Use temp directory for file operations")
72
+ suggestions.append("Avoid system directories")
73
+
74
+ # Memory errors
75
+ elif "MemoryError" in error_message or "killed" in error_message.lower():
76
+ error_type = "memory_error"
77
+ suggestions.append("Reduce data size or use chunks")
78
+ suggestions.append("Process data in smaller batches")
79
+
80
+ # File not found
81
+ elif "FileNotFoundError" in error_message or "No such file" in error_message:
82
+ error_type = "file_error"
83
+ suggestions.append("Check file paths")
84
+ suggestions.append("Create directory if needed")
85
+
86
+ # Syntax errors
87
+ elif "SyntaxError" in error_message:
88
+ error_type = "syntax_error"
89
+ suggestions.append("Fix syntax issues")
90
+ suggestions.append("Check indentation")
91
+
92
+ # Attribute errors
93
+ elif "AttributeError" in error_message:
94
+ error_type = "attribute_error"
95
+ suggestions.append("Check method/attribute names")
96
+ suggestions.append("Verify object types")
97
+
98
+ # Type errors
99
+ elif "TypeError" in error_message:
100
+ error_type = "type_error"
101
+ suggestions.append("Check data types")
102
+ suggestions.append("Add type conversions")
103
+
104
+ # Value errors
105
+ elif "ValueError" in error_message:
106
+ error_type = "value_error"
107
+ suggestions.append("Validate input data")
108
+ suggestions.append("Add error handling")
109
+
110
+ # Network errors
111
+ elif "URLError" in error_message or "ConnectionError" in error_message:
112
+ error_type = "network_error"
113
+ suggestions.append("Check internet connection")
114
+ suggestions.append("Add retry logic")
115
+
116
+ # Package specific errors
117
+ if "openpyxl" in error_message or "xlrd" in error_message:
118
+ packages_to_install.append("openpyxl")
119
+ suggestions.append("Install Excel support: openpyxl")
120
+
121
+ if "PIL" in error_message or "Pillow" in error_message:
122
+ packages_to_install.append("Pillow")
123
+ suggestions.append("Install image processing: Pillow")
124
+
125
+ return {
126
+ "error_type": error_type,
127
+ "suggestions": suggestions,
128
+ "packages": packages_to_install,
129
+ "original_error": error_message
130
+ }
131
+
132
  def indent_code(code, spaces=4):
133
  """Indent the code by the specified number of spaces."""
134
  indented_lines = []
 
163
 
164
  # Usage patterns that require additional packages
165
  usage_patterns = {
166
+ r'\.to_excel': 'openpyxl',
167
+ r'\.read_excel': 'openpyxl',
168
+ r'\.ExcelWriter': 'openpyxl',
169
+ r'\.to_parquet': 'pyarrow',
170
  r'\.read_parquet': 'pyarrow',
171
+ r'\.to_sql': 'sqlalchemy',
172
  r'\.read_sql': 'sqlalchemy',
173
+ r'\.to_feather': 'pyarrow',
174
  r'\.read_feather': 'pyarrow',
175
+ r'\.to_stata': 'statsmodels',
176
  r'\.read_stata': 'statsmodels',
177
+ r'\.to_clipboard': 'pyperclip',
178
+ r'xlsxwriter': 'xlsxwriter',
179
+ r'openpyxl': 'openpyxl',
180
  }
181
 
182
  # Check for pip install comments
 
249
  for pattern, package in usage_patterns.items():
250
  if re.search(pattern, code, re.IGNORECASE) and package not in pre_installed:
251
  required_packages.add(package)
 
252
 
253
  # Special case: if pandas is imported and Excel operations detected
254
  if 'pandas' in code and any(pattern in code for pattern in ['.to_excel', '.read_excel', 'ExcelWriter']):
255
  if 'openpyxl' not in pre_installed:
256
  required_packages.add('openpyxl')
 
257
 
258
  # Remove pre-installed packages
259
  required_packages = required_packages - pre_installed
 
281
  spec = importlib.util.find_spec(import_name)
282
  if spec is None:
283
  print(f"Installing package: {package_name}")
 
284
  result = subprocess.run([
285
  sys.executable, "-m", "pip", "install", "--quiet", "--no-cache-dir", package_name
286
  ], capture_output=True, text=True)
 
321
 
322
  return len(failed_packages) == 0
323
 
324
+ def generate_code_with_openrouter(instruction, file_paths, previous_errors=None, attempt=1):
325
+ """Generate Python code using OpenRouter API with error awareness."""
326
+
327
+ error_context = ""
328
+ if previous_errors:
329
+ error_context = "\n\nPREVIOUS ERRORS TO AVOID:\n"
330
+ for err in previous_errors:
331
+ error_context += f"- Error type: {err.get('error_type', 'unknown')}\n"
332
+ error_context += f" Details: {err.get('original_error', '')[:200]}\n"
333
+ error_context += f" Suggestions: {', '.join(err.get('suggestions', []))}\n"
334
+
335
+ alternative_approaches = ""
336
+ if attempt > 1:
337
+ alternative_approaches = f"\n\nThis is attempt {attempt}. Please use a different approach:"
338
+ if attempt == 2:
339
+ alternative_approaches += "\n- Use simpler libraries if possible"
340
+ alternative_approaches += "\n- Add more error handling"
341
+ alternative_approaches += "\n- Check file paths carefully"
342
+ elif attempt >= 3:
343
+ alternative_approaches += "\n- Use only standard library if possible"
344
+ alternative_approaches += "\n- Implement fallback solutions"
345
+ alternative_approaches += "\n- Generate mock data if files are problematic"
346
+
347
  prompt_template = textwrap.dedent("""
348
  You are a Python expert. Instruction: "{instruction}"
349
  Input files: {file_paths_str} (use file_paths[0] for first file, iterate for multiple; if empty, generate based on instruction alone).
350
+ {error_context}
351
+ {alternative_approaches}
352
 
353
  Write a complete Python script that:
354
  1. Import all necessary libraries at the top.
355
+ 2. Add "# pip install package_name" comments after imports for all needed libraries.
356
  3. Define file_paths = {file_paths_list}
357
+ 4. Add comprehensive error handling with try-except blocks
358
+ 5. Create output directory if needed using os.makedirs(exist_ok=True)
359
+ 6. Save output to a temp directory using tempfile.mkdtemp()
360
+ 7. Print "OUTPUT_FILE_PATH: /full/path/to/output" at the end using os.path.abspath()
361
+ 8. If file operations fail, try alternative approaches
362
 
363
  Important rules:
364
  - For pandas Excel operations, always add: # pip install openpyxl
365
+ - Always use absolute paths with os.path.abspath()
366
+ - Create directories before saving files
367
+ - Handle common errors (FileNotFoundError, PermissionError, etc.)
368
+ - If a library fails, try alternatives (e.g., csv instead of pandas)
369
  - No __name__ == '__main__', no functions, just direct code
370
+ - Add detailed error messages
371
 
372
+ Example with error handling:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  import os
374
  import tempfile
375
+ try:
376
+ import pandas as pd
377
+ # pip install pandas openpyxl
378
+ except ImportError:
379
+ print("Pandas not available, using csv module")
380
+ import csv
381
 
382
  file_paths = {file_paths_list}
 
 
 
383
 
384
+ try:
385
+ temp_dir = tempfile.mkdtemp()
386
+ os.makedirs(temp_dir, exist_ok=True)
387
+ output_path = os.path.join(temp_dir, 'output.xlsx')
388
+
389
+ # Your main logic here with error handling
390
+
391
+ print(f"OUTPUT_FILE_PATH: {{os.path.abspath(output_path)}}")
392
+ except Exception as e:
393
+ print(f"ERROR: {{e}}")
394
+ # Fallback solution
395
+ try:
396
+ # Alternative approach
397
+ pass
398
+ except:
399
+ print("All approaches failed")
400
 
401
  Output ONLY Python code, no markdown.
402
  """)
 
407
  prompt = prompt_template.format(
408
  instruction=instruction or "No instruction provided",
409
  file_paths_str=file_paths_str,
410
+ file_paths_list=file_paths_list,
411
+ error_context=error_context,
412
+ alternative_approaches=alternative_approaches
413
  )
414
 
415
  try:
416
  response = client.chat.completions.create(
417
  model=MODEL_NAME,
418
  messages=[
419
+ {"role": "system", "content": "Output only clean executable Python code with comprehensive error handling."},
420
  {"role": "user", "content": prompt}
421
  ],
422
  max_tokens=4000,
423
+ temperature=0.1 if attempt == 1 else 0.3 # Increase creativity on retries
424
  )
425
 
426
  generated_code = response.choices[0].message.content.strip()
 
438
  print("OUTPUT_TEXT: Code generation failed due to API error")
439
  sys.exit(0)"""
440
 
441
+ def execute_code_with_retry(code: str, max_attempts: int = 3) -> Tuple[bool, str, Optional[str]]:
442
+ """Execute code with retry logic and error recovery"""
443
  tf_path = None
444
+ attempt = 0
445
+
446
+ while attempt < max_attempts:
447
+ attempt += 1
448
+ print(f"\n=== Execution attempt {attempt}/{max_attempts} ===")
449
+
450
+ try:
451
+ # Step 1: Detect and install packages
452
+ print("Detecting packages...")
453
+ required_packages = detect_required_packages(code)
454
+ print("Detected packages:", required_packages)
455
+ install_packages_if_needed(required_packages)
456
+
457
+ # Step 2: Wrap code
458
+ indented = indent_code(code)
459
+ wrapped_code = f"try:\n{indented}\nexcept Exception as e:\n print(f'ERROR: {{e}}')\n import traceback; traceback.print_exc()\n import sys; sys.exit(1)"
460
+
461
+ # Step 3: Compile check
462
+ print("Compiling code...")
463
+ try:
464
+ compile(wrapped_code, '<string>', 'exec')
465
+ print("Compile OK.")
466
+ except SyntaxError as se:
467
+ error_msg = f"Syntax Error: {se}"
468
+ if attempt < max_attempts:
469
+ print(f"Syntax error on attempt {attempt}, will regenerate code")
470
+ return False, error_msg, None
471
+ else:
472
+ return False, error_msg, None
473
+
474
+ # Step 4: Create temp file
475
+ print("Creating temp file...")
476
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as tf:
477
+ tf.write(wrapped_code)
478
+ tf_path = tf.name
479
+ print(f"Temp file: {tf_path}")
480
+
481
+ # Step 5: Execute
482
+ print(f"Executing...")
483
+ result = subprocess.run(
484
+ [sys.executable, tf_path],
485
+ capture_output=True,
486
+ text=True,
487
+ timeout=60
488
+ )
489
+ stdout, stderr = result.stdout, result.stderr
490
+ rc = result.returncode
491
+
492
+ # Clean up temp file
493
+ if tf_path and os.path.exists(tf_path):
494
+ try:
495
+ os.unlink(tf_path)
496
+ except:
497
+ pass
498
+
499
+ if rc != 0:
500
+ error_msg = f"Execution failed (RC {rc}):\nStderr: {stderr}"
501
+ print(error_msg)
502
+
503
+ # Check if we should retry
504
+ if attempt < max_attempts:
505
+ # Analyze error for next attempt
506
+ error_analysis = ErrorAnalyzer.analyze_error(stderr, code)
507
+
508
+ # Try to fix by installing missing packages
509
+ if error_analysis['packages']:
510
+ print(f"Attempting to install missing packages: {error_analysis['packages']}")
511
+ for pkg in error_analysis['packages']:
512
+ install_package(pkg)
513
+
514
+ return False, stderr, None
515
+ else:
516
+ return False, error_msg, None
517
+
518
+ # Extract output
519
+ output_path_match = re.search(r'OUTPUT_FILE_PATH:\s*(.+)', stdout, re.I)
520
+ output_text_match = re.search(r'OUTPUT_TEXT:\s*(.+)', stdout, re.I | re.DOTALL)
521
+
522
+ if output_path_match:
523
+ output_path = output_path_match.group(1).strip()
524
+ if os.path.exists(output_path):
525
+ return True, stdout, output_path
526
+ else:
527
+ error_msg = f"Output path not found: {output_path}"
528
+ if attempt < max_attempts:
529
+ print(error_msg)
530
+ return False, error_msg, None
531
+ else:
532
+ return False, error_msg, None
533
+ elif output_text_match:
534
+ return True, output_text_match.group(1).strip(), None
535
+ else:
536
+ if stdout.strip():
537
+ return True, stdout, None
538
+ else:
539
+ return False, "No output generated", None
540
+
541
+ except subprocess.TimeoutExpired:
542
+ error_msg = "Timeout: Code execution took too long"
543
+ if attempt < max_attempts:
544
+ print(error_msg)
545
+ return False, error_msg, None
546
+ else:
547
+ return False, error_msg, None
548
+ except Exception as e:
549
+ error_msg = f"Execution error: {str(e)}"
550
+ if attempt < max_attempts:
551
+ print(error_msg)
552
+ return False, error_msg, None
553
+ else:
554
+ return False, error_msg, None
555
+
556
+ return False, "Max attempts reached", None
557
+
558
+ def process_request(instruction, files):
559
+ """Main processing function with self-correction"""
560
  try:
561
  if not instruction.strip():
562
  return "لطفاً دستور را وارد کنید. (فایل‌ها اختیاری هستند)", None
563
 
564
  file_paths = [f.name for f in files] if files else []
565
 
566
+ # Track errors for learning
567
+ previous_errors = []
568
+ generated_codes = []
569
 
570
+ # Main retry loop
571
+ for attempt in range(1, 4): # 3 attempts
572
+ print(f"\n{'='*50}")
573
+ print(f"MAIN ATTEMPT {attempt}/3")
574
+ print(f"{'='*50}")
575
+
576
+ # Generate code
577
+ print("Generating code...")
578
+ generated_code = generate_code_with_openrouter(
579
+ instruction,
580
+ file_paths,
581
+ previous_errors=previous_errors if attempt > 1 else None,
582
+ attempt=attempt
583
+ )
584
+
585
+ if len(generated_code) < 20:
586
+ return f"کد ضعیف تولید شد: {generated_code}", None
587
+
588
+ generated_codes.append(generated_code)
589
+ print(f"Generated code preview: {generated_code[:200]}...")
590
+
591
+ # Try to execute
592
+ success, output, file_path = execute_code_with_retry(generated_code, max_attempts=2)
593
+
594
+ if success:
595
+ # Success!
596
+ result_text = f"✅ Success on attempt {attempt}!\n\n"
597
+ result_text += f"Generated Code:\n```python\n{generated_code}\n```\n\n"
598
+ result_text += f"Output:\n{output}"
599
+ return result_text, file_path
600
+ else:
601
+ # Analyze error
602
+ print(f"\n❌ Attempt {attempt} failed")
603
+ error_analysis = ErrorAnalyzer.analyze_error(output, generated_code)
604
+ previous_errors.append(error_analysis)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605
 
606
+ print(f"Error type: {error_analysis['error_type']}")
607
+ print(f"Suggestions: {', '.join(error_analysis['suggestions'])}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
 
609
+ # If this was the last attempt
610
+ if attempt == 3:
611
+ error_report = f"❌ Failed after {attempt} attempts.\n\n"
612
+ error_report += "Error History:\n"
613
+ for i, err in enumerate(previous_errors, 1):
614
+ error_report += f"\nAttempt {i}:\n"
615
+ error_report += f"- Error type: {err['error_type']}\n"
616
+ error_report += f"- Details: {err['original_error'][:200]}...\n"
617
+
618
+ error_report += f"\n\nLast generated code:\n```python\n{generated_code}\n```"
619
+ return error_report, None
620
+
621
+ return "Unexpected end of retry loop", None
622
+
623
  except Exception as e:
624
  error_msg = f"General error: {type(e).__name__}: {e}\nFull traceback: {traceback.format_exc()}"
625
  print(error_msg)
626
  return error_msg, None
 
 
 
 
 
 
 
627
 
628
  # Gradio Interface
629
+ with gr.Blocks(title="AI File Processor - Self Correcting") as demo:
630
+ gr.Markdown("""
631
+ # 🤖 AI File Processor - Self Correcting Edition
632
+
633
+ این سیستم می‌تواند:
634
+ - کد Python تولید کند
635
+ - خطاها را تشخیص و تحلیل کند
636
+ - به طور خودکار مشکلات را برطرف کند
637
+ - تا 3 بار با رویکردهای مختلف تلاش کند
638
+
639
+ **مثال دستورات:**
640
+ - "یک فایل اکسل با 1000 نام و شماره تلفن ایرانی بساز"
641
+ - "پس‌زمینه این تصویر را حذف کن"
642
+ - "این فایل CSV را به JSON تبدیل کن"
643
+ """)
644
+
645
+ with gr.Row():
646
+ instruction = gr.Textbox(
647
+ label="دستور",
648
+ lines=2,
649
+ placeholder="مثال: یک نمودار دایره‌ای از داده‌های فروش بکش",
650
+ rtl=True
651
+ )
652
+ files = gr.File(file_count="multiple", label="فایل‌ها (اختیاری)")
653
+
654
+ btn = gr.Button("🚀 اجرا", variant="primary")
655
 
656
  with gr.Row():
657
+ output = gr.Textbox(label="نتیجه", lines=15)
658
+ file_out = gr.File(label="فایل خروجی (در صورت تولید)")
659
 
660
+ # Examples
661
+ gr.Examples(
662
+ examples=[
663
+ ["یک فایل اکسل با 100 محصول فروشگاهی شامل نام، قیمت و موجودی بساز", []],
664
+ ["یک نمودار میله‌ای از داده‌های تصادفی رسم کن", []],
665
+ ["یک تصویر 500x500 پیکسل با رنگ‌های تصادفی بساز", []],
666
+ ],
667
+ inputs=[instruction, files],
668
+ )
669
 
670
  btn.click(fn=process_request, inputs=[instruction, files], outputs=[output, file_out])
671