suprimedev commited on
Commit
2c9341e
·
verified ·
1 Parent(s): fb707c0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +178 -256
app.py CHANGED
@@ -14,127 +14,13 @@ import traceback
14
  import json
15
  from typing import List, Tuple, Optional, Dict
16
  import requests
17
- import threading
18
- import time
19
- from datetime import datetime, timedelta
20
- import queue
21
  import concurrent.futures
22
- from functools import wraps
23
- import atexit
24
-
25
- # Configuration for TalkGPT upload
26
- TALKGPT_API_URL = "https://talkgpt.ir/api/LUMA/upload.php"
27
- TALKGPT_API_KEY = os.getenv("TALKGPT_API_KEY", "YOUR_STRONG_SECRET_API_KEY_12345")
28
-
29
- # Clean up old files every hour
30
- CLEANUP_INTERVAL = 3600 # 1 hour
31
- FILE_MAX_AGE = 86400 # 24 hours
32
-
33
- # Track created files
34
- created_files = queue.Queue()
35
- created_files_lock = threading.Lock()
36
-
37
- # Executor for concurrent processing
38
- executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)
39
-
40
- def cleanup_old_files():
41
- """Clean up files older than 24 hours"""
42
- while True:
43
- try:
44
- current_time = time.time()
45
- temp_dir = tempfile.gettempdir()
46
-
47
- # Clean temp directory
48
- for item in os.listdir(temp_dir):
49
- item_path = os.path.join(temp_dir, item)
50
- try:
51
- if os.path.isfile(item_path):
52
- if current_time - os.path.getmtime(item_path) > FILE_MAX_AGE:
53
- os.unlink(item_path)
54
- print(f"Deleted old file: {item_path}")
55
- elif os.path.isdir(item_path) and item.startswith(('tmp', 'url_downloads_')):
56
- if current_time - os.path.getmtime(item_path) > FILE_MAX_AGE:
57
- shutil.rmtree(item_path)
58
- print(f"Deleted old directory: {item_path}")
59
- except Exception as e:
60
- print(f"Error cleaning {item_path}: {e}")
61
-
62
- # Clean tracked files
63
- cleaned_files = []
64
- with created_files_lock:
65
- while not created_files.empty():
66
- file_info = created_files.get()
67
- if current_time - file_info['time'] > FILE_MAX_AGE:
68
- try:
69
- if os.path.exists(file_info['path']):
70
- if os.path.isfile(file_info['path']):
71
- os.unlink(file_info['path'])
72
- elif os.path.isdir(file_info['path']):
73
- shutil.rmtree(file_info['path'])
74
- print(f"Deleted tracked file: {file_info['path']}")
75
- except Exception as e:
76
- print(f"Error deleting tracked file {file_info['path']}: {e}")
77
- else:
78
- cleaned_files.append(file_info)
79
-
80
- # Put back files that are not old enough
81
- for file_info in cleaned_files:
82
- created_files.put(file_info)
83
-
84
- except Exception as e:
85
- print(f"Cleanup error: {e}")
86
-
87
- time.sleep(CLEANUP_INTERVAL)
88
-
89
- # Start cleanup thread
90
- cleanup_thread = threading.Thread(target=cleanup_old_files, daemon=True)
91
- cleanup_thread.start()
92
-
93
- def track_file(file_path):
94
- """Track a created file for cleanup"""
95
- with created_files_lock:
96
- created_files.put({
97
- 'path': file_path,
98
- 'time': time.time()
99
- })
100
-
101
- def upload_to_talkgpt(file_path):
102
- """Upload file to TalkGPT and return the URL"""
103
- try:
104
- if not os.path.exists(file_path):
105
- return None, "File not found"
106
-
107
- with open(file_path, 'rb') as f:
108
- files = {'file': (os.path.basename(file_path), f)}
109
- data = {'api_key': TALKGPT_API_KEY}
110
- headers = {'X-API-KEY': TALKGPT_API_KEY}
111
-
112
- response = requests.post(
113
- TALKGPT_API_URL,
114
- files=files,
115
- data=data,
116
- headers=headers,
117
- timeout=300 # 5 minutes timeout for large files
118
- )
119
-
120
- if response.status_code == 200:
121
- result = response.json()
122
- if result.get('success'):
123
- file_url = result.get('file_url')
124
- expiry = result.get('expiry_timestamp', time.time() + 86400)
125
- expiry_date = datetime.fromtimestamp(expiry).strftime('%Y-%m-%d %H:%M:%S')
126
- return file_url, f"Expires: {expiry_date}"
127
- else:
128
- return None, result.get('message', 'Upload failed')
129
- else:
130
- return None, f"HTTP {response.status_code}: {response.text}"
131
-
132
- except requests.exceptions.Timeout:
133
- return None, "Upload timeout - file may be too large"
134
- except Exception as e:
135
- return None, f"Upload error: {str(e)}"
136
 
137
- # Clean up any existing temp files on startup
138
  try:
139
  tempdir = tempfile.gettempdir()
140
  for item in os.listdir(tempdir):
@@ -166,6 +52,14 @@ client = openai.OpenAI(
166
 
167
  MODEL_NAME = "x-ai/grok-4-fast"
168
 
 
 
 
 
 
 
 
 
169
  class ErrorAnalyzer:
170
  """Analyze errors and suggest fixes"""
171
 
@@ -380,48 +274,52 @@ def detect_required_packages(code):
380
 
381
  return list(required_packages)
382
 
 
 
 
383
  def install_package(package_name):
384
  """Install a package using pip if it's not already installed."""
385
- try:
386
- # Special handling for some packages
387
- import_name = package_name
388
- if package_name == 'opencv-python':
389
- import_name = 'cv2'
390
- elif package_name == 'scikit-learn':
391
- import_name = 'sklearn'
392
- elif package_name == 'pillow' or package_name == 'Pillow':
393
- import_name = 'PIL'
394
- elif package_name == 'beautifulsoup4':
395
- import_name = 'bs4'
396
- elif package_name == 'psycopg2-binary':
397
- import_name = 'psycopg2'
398
- else:
399
- import_name = package_name.replace('-', '_')
400
-
401
- spec = importlib.util.find_spec(import_name)
402
- if spec is None:
403
- print(f"Installing package: {package_name}")
404
- result = subprocess.run([
405
- sys.executable, "-m", "pip", "install", "--quiet", "--no-cache-dir", package_name
406
- ], capture_output=True, text=True)
407
-
408
- if result.returncode == 0:
409
- print(f"✅ {package_name} installed successfully.")
410
- return True
411
  else:
412
- print(f"❌ Failed to install {package_name}: {result.stderr}")
413
- return False
414
- else:
415
- print(f"✅ {package_name} already installed.")
416
- return True
417
- except Exception as e:
418
- print(f" Error checking/installing {package_name}: {str(e)}")
419
- return False
 
 
 
 
 
 
 
 
 
 
 
 
 
420
 
421
  def install_packages_if_needed(packages):
422
  """Install required packages."""
423
  if not packages:
424
- print("No additional packages to install.")
425
  return True
426
 
427
  success_count = 0
@@ -434,10 +332,10 @@ def install_packages_if_needed(packages):
434
  else:
435
  failed_packages.append(package)
436
 
437
- print(f"✅ Installed/checked {success_count}/{len(packages)} packages.")
438
 
439
  if failed_packages:
440
- print(f"⚠️ Failed to install: {', '.join(failed_packages)}")
441
 
442
  return len(failed_packages) == 0
443
 
@@ -456,11 +354,10 @@ def download_file_from_url(url: str, temp_dir: str) -> Optional[str]:
456
  for chunk in response.iter_content(chunk_size=8192):
457
  f.write(chunk)
458
 
459
- print(f"Downloaded {url} to {local_path}")
460
- track_file(local_path)
461
  return local_path
462
  except Exception as e:
463
- print(f"Failed to download {url}: {e}")
464
  return None
465
 
466
  def generate_code_with_openrouter(instruction, file_paths, previous_errors=None, attempt=1):
@@ -574,7 +471,7 @@ Output ONLY Python code, no markdown.
574
 
575
  except Exception as api_error:
576
  error_msg = f"API Error: {api_error}"
577
- print(error_msg)
578
  # Return simple fallback
579
  return """import sys
580
  print("OUTPUT_TEXT: Code generation failed due to API error")
@@ -582,18 +479,19 @@ sys.exit(0)"""
582
 
583
  def execute_code_with_retry(code: str, max_attempts: int = 3) -> Tuple[bool, str, Optional[str]]:
584
  """Execute code with retry logic and error recovery"""
 
585
  tf_path = None
586
  attempt = 0
587
 
588
  while attempt < max_attempts:
589
  attempt += 1
590
- print(f"\n=== Execution attempt {attempt}/{max_attempts} ===")
591
 
592
  try:
593
  # Step 1: Detect and install packages
594
- print("Detecting packages...")
595
  required_packages = detect_required_packages(code)
596
- print("Detected packages:", required_packages)
597
  install_packages_if_needed(required_packages)
598
 
599
  # Step 2: Wrap code
@@ -601,28 +499,27 @@ def execute_code_with_retry(code: str, max_attempts: int = 3) -> Tuple[bool, str
601
  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)"
602
 
603
  # Step 3: Compile check
604
- print("Compiling code...")
605
  try:
606
  compile(wrapped_code, '<string>', 'exec')
607
- print("Compile OK.")
608
  except SyntaxError as se:
609
  error_msg = f"Syntax Error: {se}"
610
  if attempt < max_attempts:
611
- print(f"Syntax error on attempt {attempt}, will regenerate code")
612
  return False, error_msg, None
613
  else:
614
  return False, error_msg, None
615
 
616
  # Step 4: Create temp file
617
- print("Creating temp file...")
618
  with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as tf:
619
  tf.write(wrapped_code)
620
  tf_path = tf.name
621
- print(f"Temp file: {tf_path}")
622
- track_file(tf_path)
623
 
624
  # Step 5: Execute
625
- print(f"Executing...")
626
  result = subprocess.run(
627
  [sys.executable, tf_path],
628
  capture_output=True,
@@ -641,7 +538,7 @@ def execute_code_with_retry(code: str, max_attempts: int = 3) -> Tuple[bool, str
641
 
642
  if rc != 0:
643
  error_msg = f"Execution failed (RC {rc}):\nStderr: {stderr}"
644
- print(error_msg)
645
 
646
  # Check if we should retry
647
  if attempt < max_attempts:
@@ -650,7 +547,7 @@ def execute_code_with_retry(code: str, max_attempts: int = 3) -> Tuple[bool, str
650
 
651
  # Try to fix by installing missing packages
652
  if error_analysis['packages']:
653
- print(f"Attempting to install missing packages: {error_analysis['packages']}")
654
  for pkg in error_analysis['packages']:
655
  install_package(pkg)
656
 
@@ -665,12 +562,11 @@ def execute_code_with_retry(code: str, max_attempts: int = 3) -> Tuple[bool, str
665
  if output_path_match:
666
  output_path = output_path_match.group(1).strip()
667
  if os.path.exists(output_path):
668
- track_file(output_path)
669
  return True, stdout, output_path
670
  else:
671
  error_msg = f"Output path not found: {output_path}"
672
  if attempt < max_attempts:
673
- print(error_msg)
674
  return False, error_msg, None
675
  else:
676
  return False, error_msg, None
@@ -685,25 +581,28 @@ def execute_code_with_retry(code: str, max_attempts: int = 3) -> Tuple[bool, str
685
  except subprocess.TimeoutExpired:
686
  error_msg = "Timeout: Code execution took too long"
687
  if attempt < max_attempts:
688
- print(error_msg)
689
  return False, error_msg, None
690
  else:
691
  return False, error_msg, None
692
  except Exception as e:
693
  error_msg = f"Execution error: {str(e)}"
694
  if attempt < max_attempts:
695
- print(error_msg)
696
  return False, error_msg, None
697
  else:
698
  return False, error_msg, None
699
 
700
  return False, "Max attempts reached", None
701
 
702
- def process_request_sync(instruction, files, urls_input):
703
- """Synchronous processing function"""
 
 
 
704
  try:
705
  if not instruction.strip():
706
- return "لطفاً دستور را وارد کنید. (فایل‌ها و لینک‌ها اختیاری هستند)", None, None
707
 
708
  file_paths = []
709
 
@@ -714,7 +613,6 @@ def process_request_sync(instruction, files, urls_input):
714
  # Handle URLs: download to temp dir
715
  if urls_input and urls_input.strip():
716
  temp_dir_for_downloads = tempfile.mkdtemp(prefix='url_downloads_')
717
- track_file(temp_dir_for_downloads)
718
  urls = [url.strip() for url in urls_input.split(',') if url.strip()]
719
  downloaded_paths = []
720
  for url in urls:
@@ -722,7 +620,7 @@ def process_request_sync(instruction, files, urls_input):
722
  if local_path:
723
  downloaded_paths.append(local_path)
724
  file_paths.extend(downloaded_paths)
725
- print(f"Downloaded {len(downloaded_paths)} files from URLs")
726
 
727
  # Track errors for learning
728
  previous_errors = []
@@ -730,12 +628,12 @@ def process_request_sync(instruction, files, urls_input):
730
 
731
  # Main retry loop
732
  for attempt in range(1, 4): # 3 attempts
733
- print(f"\n{'='*50}")
734
- print(f"MAIN ATTEMPT {attempt}/3")
735
- print(f"{'='*50}")
736
 
737
  # Generate code
738
- print("Generating code...")
739
  generated_code = generate_code_with_openrouter(
740
  instruction,
741
  file_paths,
@@ -744,43 +642,29 @@ def process_request_sync(instruction, files, urls_input):
744
  )
745
 
746
  if len(generated_code) < 20:
747
- return f"کد ضعیف تولید شد: {generated_code}", None, None
748
 
749
  generated_codes.append(generated_code)
750
- print(f"Generated code preview: {generated_code[:200]}...")
751
 
752
  # Try to execute
753
  success, output, file_path = execute_code_with_retry(generated_code, max_attempts=2)
754
 
755
  if success:
756
- # Success! Upload to TalkGPT if file was generated
757
- talkgpt_url = None
758
- talkgpt_message = ""
759
- if file_path and os.path.exists(file_path):
760
- print("Uploading to TalkGPT...")
761
- talkgpt_url, talkgpt_message = upload_to_talkgpt(file_path)
762
- if talkgpt_url:
763
- result_text = f"✅ Success on attempt {attempt}!\n\n"
764
- result_text += f"📁 TalkGPT URL: {talkgpt_url}\n"
765
- result_text += f"📅 {talkgpt_message}\n\n"
766
- else:
767
- result_text = f"✅ Success on attempt {attempt}!\n\n"
768
- result_text += f"⚠️ Upload to TalkGPT failed: {talkgpt_message}\n\n"
769
- else:
770
- result_text = f"✅ Success on attempt {attempt}!\n\n"
771
-
772
  result_text += f"Generated Code:\n```python\n{generated_code}\n```\n\n"
773
  result_text += f"Output:\n{output}"
774
-
775
- return result_text, file_path, talkgpt_url
776
  else:
777
  # Analyze error
778
- print(f"\n❌ Attempt {attempt} failed")
779
  error_analysis = ErrorAnalyzer.analyze_error(output, generated_code)
780
  previous_errors.append(error_analysis)
781
 
782
- print(f"Error type: {error_analysis['error_type']}")
783
- print(f"Suggestions: {', '.join(error_analysis['suggestions'])}")
784
 
785
  # If this was the last attempt
786
  if attempt == 3:
@@ -792,63 +676,66 @@ def process_request_sync(instruction, files, urls_input):
792
  error_report += f"- Details: {err['original_error'][:200]}...\n"
793
 
794
  error_report += f"\n\nLast generated code:\n```python\n{generated_code}\n```"
795
- return error_report, None, None
 
796
 
797
- return "Unexpected end of retry loop", None, None
798
 
799
  except Exception as e:
800
  error_msg = f"General error: {type(e).__name__}: {e}\nFull traceback: {traceback.format_exc()}"
801
- print(error_msg)
802
- return error_msg, None, None
803
-
804
- def process_request(instruction, files, urls_input):
805
- """Process request with concurrent execution support"""
806
- future = executor.submit(process_request_sync, instruction, files, urls_input)
807
- result_text, file_path, talkgpt_url = future.result()
808
-
809
- # Return results with TalkGPT URL as the file output
810
- # If there's a TalkGPT URL, return it as an HTML link
811
- if talkgpt_url:
812
- # Create an HTML component for the link
813
- file_output_html = f"""{talkgpt_url}"""
814
- return result_text, file_output_html
815
- else:
816
- # No file was generated or upload failed
817
- return result_text, None
818
 
819
- # Cleanup on exit
820
- def cleanup_on_exit():
821
- """Clean up when the app exits"""
822
- print("Cleaning up...")
823
- executor.shutdown(wait=True)
824
- # Final cleanup of tracked files
825
- with created_files_lock:
826
- while not created_files.empty():
827
- file_info = created_files.get()
828
- try:
829
- if os.path.exists(file_info['path']):
830
- if os.path.isfile(file_info['path']):
831
- os.unlink(file_info['path'])
832
- elif os.path.isdir(file_info['path']):
833
- shutil.rmtree(file_info['path'])
834
- except:
835
- pass
836
-
837
- atexit.register(cleanup_on_exit)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
838
 
839
- # Gradio Interface
840
- with gr.Blocks(title="AI File Processor - Self Correcting with TalkGPT Upload") as demo:
841
- gr.Markdown("""
842
- # 🤖 AI File Processor - Self Correcting Edition with TalkGPT Upload
 
 
843
 
844
  این سیستم می‌تواند:
845
  - کد Python تولید کند
846
  - خطاها را تشخیص و تحلیل کند
847
  - به طور خودکار مشکلات را برطرف کند
848
  - تا 3 بار با رویکردهای مختلف تلاش کند
849
- - فایل‌های خروجی را به TalkGPT آپلود کند (لینک 7 روزه)
850
- - چندین درخواست را همزمان پردازش کند
851
- - فایل‌های قدیمی را به طور خودکار حذف کند (بعد از 24 ساعت)
852
 
853
  **مثال دستورات:**
854
  - "یک فایل اکسل با 1000 نام و شماره تلفن ایرانی بساز"
@@ -858,6 +745,14 @@ with gr.Blocks(title="AI File Processor - Self Correcting with TalkGPT Upload")
858
  **نکته:** می‌توانید فایل‌ها را آپلود کنید یا لینک‌های فایل را (جدا شده با کاما) وارد کنید.
859
  """)
860
 
 
 
 
 
 
 
 
 
861
  with gr.Row():
862
  instruction = gr.Textbox(
863
  label="دستور",
@@ -878,7 +773,7 @@ with gr.Blocks(title="AI File Processor - Self Correcting with TalkGPT Upload")
878
 
879
  with gr.Row():
880
  output = gr.Textbox(label="نتیجه", lines=15)
881
- file_link_output = gr.HTML(label="لینک دانلود فایل")
882
 
883
  # Examples
884
  gr.Examples(
@@ -891,7 +786,34 @@ with gr.Blocks(title="AI File Processor - Self Correcting with TalkGPT Upload")
891
  inputs=[instruction, files, urls],
892
  )
893
 
894
- btn.click(fn=process_request, inputs=[instruction, files, urls], outputs=[output, file_link_output])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
895
 
896
  if __name__ == "__main__":
897
- demo.launch(share=True)
 
 
 
 
 
 
 
 
14
  import json
15
  from typing import List, Tuple, Optional, Dict
16
  import requests
17
+ import asyncio
 
 
 
18
  import concurrent.futures
19
+ from datetime import datetime
20
+ import threading
21
+ import queue as queue_module
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ # Clean up any existing temp files on startup to save space
24
  try:
25
  tempdir = tempfile.gettempdir()
26
  for item in os.listdir(tempdir):
 
52
 
53
  MODEL_NAME = "x-ai/grok-4-fast"
54
 
55
+ # Thread pool for concurrent execution
56
+ MAX_WORKERS = 5 # تعداد همزمانی را می‌توانید تنظیم کنید
57
+ executor = concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS)
58
+
59
+ # Track active requests
60
+ active_requests = {}
61
+ request_lock = threading.Lock()
62
+
63
  class ErrorAnalyzer:
64
  """Analyze errors and suggest fixes"""
65
 
 
274
 
275
  return list(required_packages)
276
 
277
+ # Package installation lock to prevent concurrent pip operations
278
+ pip_lock = threading.Lock()
279
+
280
  def install_package(package_name):
281
  """Install a package using pip if it's not already installed."""
282
+ with pip_lock: # Prevent concurrent pip operations
283
+ try:
284
+ # Special handling for some packages
285
+ import_name = package_name
286
+ if package_name == 'opencv-python':
287
+ import_name = 'cv2'
288
+ elif package_name == 'scikit-learn':
289
+ import_name = 'sklearn'
290
+ elif package_name == 'pillow' or package_name == 'Pillow':
291
+ import_name = 'PIL'
292
+ elif package_name == 'beautifulsoup4':
293
+ import_name = 'bs4'
294
+ elif package_name == 'psycopg2-binary':
295
+ import_name = 'psycopg2'
 
 
 
 
 
 
 
 
 
 
 
 
296
  else:
297
+ import_name = package_name.replace('-', '_')
298
+
299
+ spec = importlib.util.find_spec(import_name)
300
+ if spec is None:
301
+ print(f"[Thread {threading.current_thread().name}] Installing package: {package_name}")
302
+ result = subprocess.run([
303
+ sys.executable, "-m", "pip", "install", "--quiet", "--no-cache-dir", package_name
304
+ ], capture_output=True, text=True)
305
+
306
+ if result.returncode == 0:
307
+ print(f"[Thread {threading.current_thread().name}] ✅ {package_name} installed successfully.")
308
+ return True
309
+ else:
310
+ print(f"[Thread {threading.current_thread().name}] ❌ Failed to install {package_name}: {result.stderr}")
311
+ return False
312
+ else:
313
+ print(f"[Thread {threading.current_thread().name}] ✅ {package_name} already installed.")
314
+ return True
315
+ except Exception as e:
316
+ print(f"[Thread {threading.current_thread().name}] ❌ Error checking/installing {package_name}: {str(e)}")
317
+ return False
318
 
319
  def install_packages_if_needed(packages):
320
  """Install required packages."""
321
  if not packages:
322
+ print(f"[Thread {threading.current_thread().name}] No additional packages to install.")
323
  return True
324
 
325
  success_count = 0
 
332
  else:
333
  failed_packages.append(package)
334
 
335
+ print(f"[Thread {threading.current_thread().name}] ✅ Installed/checked {success_count}/{len(packages)} packages.")
336
 
337
  if failed_packages:
338
+ print(f"[Thread {threading.current_thread().name}] ⚠️ Failed to install: {', '.join(failed_packages)}")
339
 
340
  return len(failed_packages) == 0
341
 
 
354
  for chunk in response.iter_content(chunk_size=8192):
355
  f.write(chunk)
356
 
357
+ print(f"[Thread {threading.current_thread().name}] Downloaded {url} to {local_path}")
 
358
  return local_path
359
  except Exception as e:
360
+ print(f"[Thread {threading.current_thread().name}] Failed to download {url}: {e}")
361
  return None
362
 
363
  def generate_code_with_openrouter(instruction, file_paths, previous_errors=None, attempt=1):
 
471
 
472
  except Exception as api_error:
473
  error_msg = f"API Error: {api_error}"
474
+ print(f"[Thread {threading.current_thread().name}] {error_msg}")
475
  # Return simple fallback
476
  return """import sys
477
  print("OUTPUT_TEXT: Code generation failed due to API error")
 
479
 
480
  def execute_code_with_retry(code: str, max_attempts: int = 3) -> Tuple[bool, str, Optional[str]]:
481
  """Execute code with retry logic and error recovery"""
482
+ thread_name = threading.current_thread().name
483
  tf_path = None
484
  attempt = 0
485
 
486
  while attempt < max_attempts:
487
  attempt += 1
488
+ print(f"\n[Thread {thread_name}] === Execution attempt {attempt}/{max_attempts} ===")
489
 
490
  try:
491
  # Step 1: Detect and install packages
492
+ print(f"[Thread {thread_name}] Detecting packages...")
493
  required_packages = detect_required_packages(code)
494
+ print(f"[Thread {thread_name}] Detected packages:", required_packages)
495
  install_packages_if_needed(required_packages)
496
 
497
  # Step 2: Wrap code
 
499
  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)"
500
 
501
  # Step 3: Compile check
502
+ print(f"[Thread {thread_name}] Compiling code...")
503
  try:
504
  compile(wrapped_code, '<string>', 'exec')
505
+ print(f"[Thread {thread_name}] Compile OK.")
506
  except SyntaxError as se:
507
  error_msg = f"Syntax Error: {se}"
508
  if attempt < max_attempts:
509
+ print(f"[Thread {thread_name}] Syntax error on attempt {attempt}, will regenerate code")
510
  return False, error_msg, None
511
  else:
512
  return False, error_msg, None
513
 
514
  # Step 4: Create temp file
515
+ print(f"[Thread {thread_name}] Creating temp file...")
516
  with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as tf:
517
  tf.write(wrapped_code)
518
  tf_path = tf.name
519
+ print(f"[Thread {thread_name}] Temp file: {tf_path}")
 
520
 
521
  # Step 5: Execute
522
+ print(f"[Thread {thread_name}] Executing...")
523
  result = subprocess.run(
524
  [sys.executable, tf_path],
525
  capture_output=True,
 
538
 
539
  if rc != 0:
540
  error_msg = f"Execution failed (RC {rc}):\nStderr: {stderr}"
541
+ print(f"[Thread {thread_name}] {error_msg}")
542
 
543
  # Check if we should retry
544
  if attempt < max_attempts:
 
547
 
548
  # Try to fix by installing missing packages
549
  if error_analysis['packages']:
550
+ print(f"[Thread {thread_name}] Attempting to install missing packages: {error_analysis['packages']}")
551
  for pkg in error_analysis['packages']:
552
  install_package(pkg)
553
 
 
562
  if output_path_match:
563
  output_path = output_path_match.group(1).strip()
564
  if os.path.exists(output_path):
 
565
  return True, stdout, output_path
566
  else:
567
  error_msg = f"Output path not found: {output_path}"
568
  if attempt < max_attempts:
569
+ print(f"[Thread {thread_name}] {error_msg}")
570
  return False, error_msg, None
571
  else:
572
  return False, error_msg, None
 
581
  except subprocess.TimeoutExpired:
582
  error_msg = "Timeout: Code execution took too long"
583
  if attempt < max_attempts:
584
+ print(f"[Thread {thread_name}] {error_msg}")
585
  return False, error_msg, None
586
  else:
587
  return False, error_msg, None
588
  except Exception as e:
589
  error_msg = f"Execution error: {str(e)}"
590
  if attempt < max_attempts:
591
+ print(f"[Thread {thread_name}] {error_msg}")
592
  return False, error_msg, None
593
  else:
594
  return False, error_msg, None
595
 
596
  return False, "Max attempts reached", None
597
 
598
+ def process_request_sync(instruction, files, urls_input, request_id):
599
+ """Synchronous processing function that will be run in thread pool."""
600
+ thread_name = threading.current_thread().name
601
+ print(f"\n[Thread {thread_name}] Starting request {request_id}")
602
+
603
  try:
604
  if not instruction.strip():
605
+ return "لطفاً دستور را وارد کنید. (فایل‌ها و لینک‌ها اختیاری هستند)", None
606
 
607
  file_paths = []
608
 
 
613
  # Handle URLs: download to temp dir
614
  if urls_input and urls_input.strip():
615
  temp_dir_for_downloads = tempfile.mkdtemp(prefix='url_downloads_')
 
616
  urls = [url.strip() for url in urls_input.split(',') if url.strip()]
617
  downloaded_paths = []
618
  for url in urls:
 
620
  if local_path:
621
  downloaded_paths.append(local_path)
622
  file_paths.extend(downloaded_paths)
623
+ print(f"[Thread {thread_name}] Downloaded {len(downloaded_paths)} files from URLs")
624
 
625
  # Track errors for learning
626
  previous_errors = []
 
628
 
629
  # Main retry loop
630
  for attempt in range(1, 4): # 3 attempts
631
+ print(f"\n[Thread {thread_name}] {'='*50}")
632
+ print(f"[Thread {thread_name}] MAIN ATTEMPT {attempt}/3 for request {request_id}")
633
+ print(f"[Thread {thread_name}] {'='*50}")
634
 
635
  # Generate code
636
+ print(f"[Thread {thread_name}] Generating code...")
637
  generated_code = generate_code_with_openrouter(
638
  instruction,
639
  file_paths,
 
642
  )
643
 
644
  if len(generated_code) < 20:
645
+ return f"کد ضعیف تولید شد: {generated_code}", None
646
 
647
  generated_codes.append(generated_code)
648
+ print(f"[Thread {thread_name}] Generated code preview: {generated_code[:200]}...")
649
 
650
  # Try to execute
651
  success, output, file_path = execute_code_with_retry(generated_code, max_attempts=2)
652
 
653
  if success:
654
+ # Success!
655
+ result_text = f"✅ Success on attempt {attempt}!\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
  result_text += f"Generated Code:\n```python\n{generated_code}\n```\n\n"
657
  result_text += f"Output:\n{output}"
658
+ print(f"[Thread {thread_name}] Request {request_id} completed successfully")
659
+ return result_text, file_path
660
  else:
661
  # Analyze error
662
+ print(f"\n[Thread {thread_name}] ❌ Attempt {attempt} failed for request {request_id}")
663
  error_analysis = ErrorAnalyzer.analyze_error(output, generated_code)
664
  previous_errors.append(error_analysis)
665
 
666
+ print(f"[Thread {thread_name}] Error type: {error_analysis['error_type']}")
667
+ print(f"[Thread {thread_name}] Suggestions: {', '.join(error_analysis['suggestions'])}")
668
 
669
  # If this was the last attempt
670
  if attempt == 3:
 
676
  error_report += f"- Details: {err['original_error'][:200]}...\n"
677
 
678
  error_report += f"\n\nLast generated code:\n```python\n{generated_code}\n```"
679
+ print(f"[Thread {thread_name}] Request {request_id} failed after all attempts")
680
+ return error_report, None
681
 
682
+ return "Unexpected end of retry loop", None
683
 
684
  except Exception as e:
685
  error_msg = f"General error: {type(e).__name__}: {e}\nFull traceback: {traceback.format_exc()}"
686
+ print(f"[Thread {thread_name}] {error_msg}")
687
+ return error_msg, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
688
 
689
+ def process_request(instruction, files, urls_input, request):
690
+ """Async wrapper for Gradio that submits to thread pool."""
691
+ # Generate unique request ID
692
+ request_id = f"{datetime.now().strftime('%Y%m%d_%H%M%S')}_{id(request)}"
693
+
694
+ with request_lock:
695
+ active_count = len(active_requests)
696
+ if active_count >= MAX_WORKERS:
697
+ queue_position = active_count - MAX_WORKERS + 1
698
+ status_message = f"⏳ درخواست شما در صف است. موقعیت در صف: {queue_position}"
699
+ print(f"Request {request_id} queued. Position: {queue_position}")
700
+ else:
701
+ status_message = f"🚀 درخواست شما در حال پردازش است..."
702
+ print(f"Request {request_id} processing immediately")
703
+
704
+ # Submit to thread pool
705
+ future = executor.submit(process_request_sync, instruction, files, urls_input, request_id)
706
+
707
+ # Track request
708
+ with request_lock:
709
+ active_requests[request_id] = {
710
+ 'future': future,
711
+ 'start_time': datetime.now(),
712
+ 'instruction': instruction[:100] # Store first 100 chars
713
+ }
714
+
715
+ # Wait for result
716
+ try:
717
+ result = future.result() # This will block until complete
718
+ finally:
719
+ # Clean up
720
+ with request_lock:
721
+ if request_id in active_requests:
722
+ del active_requests[request_id]
723
+
724
+ return result
725
 
726
+ # Gradio Interface with queue enabled
727
+ with gr.Blocks(title="AI File Processor - Concurrent") as demo:
728
+ gr.Markdown(f"""
729
+ # 🤖 AI File Processor - Concurrent Edition
730
+
731
+ **🔥 قابلیت پردازش همزمان: تا {MAX_WORKERS} درخواست به صورت موازی**
732
 
733
  این سیستم می‌تواند:
734
  - کد Python تولید کند
735
  - خطاها را تشخیص و تحلیل کند
736
  - به طور خودکار مشکلات را برطرف کند
737
  - تا 3 بار با رویکردهای مختلف تلاش کند
738
+ - **چندین درخواست را همزمان پردازش کند**
 
 
739
 
740
  **مثال دستورات:**
741
  - "یک فایل اکسل با 1000 نام و شماره تلفن ایرانی بساز"
 
745
  **نکته:** می‌توانید فایل‌ها را آپلود کنید یا لینک‌های فایل را (جدا شده با کاما) وارد کنید.
746
  """)
747
 
748
+ # Status indicator
749
+ with gr.Row():
750
+ status_box = gr.Textbox(
751
+ label="وضعیت سیستم",
752
+ value=f"✅ آماده دریافت درخواست | حداکثر {MAX_WORKERS} درخواست همزمان",
753
+ interactive=False
754
+ )
755
+
756
  with gr.Row():
757
  instruction = gr.Textbox(
758
  label="دستور",
 
773
 
774
  with gr.Row():
775
  output = gr.Textbox(label="نتیجه", lines=15)
776
+ file_out = gr.File(label="فایل خروجی (در صورت تولید)")
777
 
778
  # Examples
779
  gr.Examples(
 
786
  inputs=[instruction, files, urls],
787
  )
788
 
789
+ # Update status periodically
790
+ def update_status():
791
+ with request_lock:
792
+ active_count = len(active_requests)
793
+ if active_count == 0:
794
+ return f"✅ آماده دریافت درخواست | حداکثر {MAX_WORKERS} درخواست همزمان"
795
+ elif active_count < MAX_WORKERS:
796
+ return f"🔄 {active_count} درخواست در حال پردازش | {MAX_WORKERS - active_count} جای خالی"
797
+ else:
798
+ return f"🔥 ظرفیت پر! {active_count} درخواست در حال پردازش | {active_count - MAX_WORKERS} در صف"
799
+
800
+ # Enable queue for concurrent processing
801
+ btn.click(
802
+ fn=process_request,
803
+ inputs=[instruction, files, urls],
804
+ outputs=[output, file_out],
805
+ queue=True # This enables queueing
806
+ )
807
+
808
+ # Periodic status update (optional - you can remove if not needed)
809
+ demo.load(update_status, outputs=status_box, every=2)
810
 
811
  if __name__ == "__main__":
812
+ # Launch with queue enabled for concurrent processing
813
+ demo.queue(
814
+ concurrency_count=MAX_WORKERS, # Number of concurrent workers
815
+ max_size=100 # Maximum queue size
816
+ ).launch(
817
+ share=True,
818
+ max_threads=MAX_WORKERS * 2 # Allow more threads for handling requests
819
+ )