JawadBenali commited on
Commit
3a50c3e
Β·
1 Parent(s): ee7688b

new commit

Browse files
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
  title: Architectai Mcp
3
- emoji: 🐨
4
  colorFrom: pink
5
  colorTo: purple
6
  sdk: gradio
 
1
  ---
2
  title: Architectai Mcp
3
+ emoji: πŸ›οΈ
4
  colorFrom: pink
5
  colorTo: purple
6
  sdk: gradio
app.py CHANGED
@@ -12,6 +12,7 @@ from pathlib import Path
12
  from PIL import Image
13
  from plantuml import PlantUML
14
 
 
15
  from services.pattern_detector import PatternDetectionService, PatternRecommendation
16
  from services.sequence_service import CallGraphVisitor, ProjectSequenceAnalyzer, SequenceDiagramService
17
  from services.usecase_service import UseCaseDiagramService
@@ -40,57 +41,6 @@ logger = logging.getLogger(__name__)
40
  PLANTUML_SERVER_URL = 'http://www.plantuml.com/plantuml/img/'
41
  plantuml_client = PlantUML(url=PLANTUML_SERVER_URL)
42
 
43
- # --- SINGLETON LLM CLIENT ---
44
- class LLMClientSingleton:
45
- """Singleton pattern for LLM client to avoid repeated connections"""
46
- _instance = None
47
- _llm_client = None
48
- _current_provider = None
49
-
50
- def __new__(cls):
51
- if cls._instance is None:
52
- cls._instance = super().__new__(cls)
53
- return cls._instance
54
-
55
- def get_client(self, preferred_provider: str = "openai", temperature: float = 0.0):
56
- """Get or create LLM client with provider fallback"""
57
- if self._llm_client is not None and self._current_provider == preferred_provider:
58
- logger.info(f"βœ… Reusing existing {preferred_provider} connection")
59
- return self._llm_client
60
-
61
- # Define provider strategies with fallbacks
62
- strategies = {
63
- "openai": [create_openai_llm, create_sambanova_llm, create_nebius_llm],
64
- "sambanova": [create_sambanova_llm, create_openai_llm, create_nebius_llm],
65
- "nebius": [create_nebius_llm, create_openai_llm, create_sambanova_llm],
66
- "gemini": [create_gemini_llm, create_sambanova_llm, create_nebius_llm]
67
- }
68
-
69
-
70
- factories = strategies.get(preferred_provider, strategies["openai"])
71
- factory_names = {
72
- create_openai_llm: "openai",
73
- create_sambanova_llm: "sambanova",
74
- create_nebius_llm: "nebius",
75
- create_gemini_llm: "gemini"
76
- }
77
-
78
- for factory in factories:
79
- provider_name = factory_names.get(factory, "unknown")
80
- try:
81
- logger.info(f"πŸ”„ Attempting to connect to {provider_name}...")
82
- self._llm_client = factory(temperature=temperature)
83
- self._current_provider = provider_name
84
- logger.info(f"βœ… Connected to {provider_name}")
85
- return self._llm_client
86
- except Exception as e:
87
- logger.warning(f"⚠️ {provider_name} failed: {str(e)[:100]}")
88
-
89
- logger.error("❌ All LLM providers failed")
90
- self._llm_client = None
91
- self._current_provider = None
92
- return None
93
-
94
 
95
  # Global singleton instance
96
  _llm_singleton = LLMClientSingleton()
@@ -170,7 +120,7 @@ def extract_file_list(zip_path):
170
  )
171
 
172
  # --- TAB 1: SINGLE FILE ANALYSIS ---
173
- def process_code_snippet_with_patterns(code_snippet: str, enrich_types: bool = False, provider: str = "sambanova"):
174
  """
175
  Analyze single Python code snippet:
176
  1. Detect design patterns and recommendations
@@ -447,7 +397,7 @@ def process_zip_upload(zip_path, progress=gr.Progress()):
447
 
448
  # -- TAB 3 : USE CASE DIAGRAM ---
449
 
450
- def process_usecase_snippet(code_snippet: str, enrich: bool = True, provider: str = "sambanova"):
451
  """TAB 1B: Single File Use Case Diagram"""
452
  if not code_snippet.strip():
453
  return "⚠️ Please enter some code.", None, gr.update(visible=False)
@@ -466,7 +416,7 @@ def process_usecase_snippet(code_snippet: str, enrich: bool = True, provider: st
466
  except Exception as e:
467
  return f"❌ Error: {e}", None, gr.update(visible=True, value=f"❌ Error")
468
 
469
- def process_folder_usecase(folder_path: str, enrich: bool = True, provider: str = "sambanova", progress=gr.Progress()):
470
  """TAB 2B: Project Use Case Diagram - SINGLE COMBINED"""
471
  path_obj = Path(folder_path)
472
 
@@ -512,7 +462,7 @@ def process_folder_usecase(folder_path: str, enrich: bool = True, provider: str
512
  except Exception as e:
513
  return f"❌ Error: {e}", None, gr.update(visible=True, value=f"❌ Failed")
514
 
515
- def process_folder_usecase_multi(folder_path: str, enrich: bool = True, provider: str = "sambanova", progress=gr.Progress()):
516
  """TAB 3: Project Use Case Diagrams - MULTIPLE BY MODULE"""
517
  path_obj = Path(folder_path)
518
 
@@ -600,7 +550,7 @@ def process_folder_usecase_multi(folder_path: str, enrich: bool = True, provider
600
  logging.error(f"Multi-diagram error: {error_detail}")
601
  return f"❌ Error: {e}\n\nDetails:\n{error_detail}", [], [], None, "", gr.update(visible=True, value=f"❌ Failed")
602
 
603
- def process_folder_usecase_multi_zip(zip_path, enrich: bool = True, provider: str = "sambanova", progress=gr.Progress()):
604
  """TAB 3: Multi-Module Use Cases from ZIP file"""
605
 
606
  # βœ… FIX: Check if zip_path is provided and is a valid file
@@ -670,7 +620,7 @@ def process_folder_usecase_multi_zip(zip_path, enrich: bool = True, provider: st
670
  if "error" in diagrams_dict:
671
  return diagrams_dict["error"], [], [], None, "", gr.update(visible=True, value="❌ Failed")
672
 
673
- progress(0.8, desc="🎨 Rendering diagrams...")
674
 
675
  diagram_outputs = []
676
 
@@ -715,10 +665,8 @@ def process_folder_usecase_multi_zip(zip_path, enrich: bool = True, provider: st
715
  finally:
716
  safe_cleanup(temp_dir)
717
 
718
-
719
-
720
  # --- TAB 4: sequence diagrams ---
721
- def process_folder_sequence_multi_zip(zip_path, enrich: bool = False, provider: str = "sambanova", progress=gr.Progress()):
722
  """TAB 4: Multi-Module Sequences from ZIP file"""
723
 
724
  # βœ… FIX: Check if zip_path is provided and is a valid file
@@ -834,7 +782,7 @@ def process_folder_sequence_multi_zip(zip_path, enrich: bool = False, provider:
834
  finally:
835
  safe_cleanup(temp_dir)
836
 
837
- def process_sequence_snippet(code_snippet: str, entry_method: str = None, enrich: bool = True, provider: str = "sambanova"):
838
  """TAB 1C: Single File Sequence Diagram"""
839
  if not code_snippet.strip():
840
  return "⚠️ Please enter some code.", None, gr.update(visible=False), ""
@@ -860,7 +808,7 @@ def process_sequence_snippet(code_snippet: str, entry_method: str = None, enrich
860
  except Exception as e:
861
  return f"❌ Error: {e}", None, gr.update(visible=True, value=f"❌ Error"), ""
862
 
863
- def process_folder_sequence(folder_path: str, entry_method: str = None, enrich: bool = False, provider: str = "sambanova", progress=gr.Progress()):
864
  """TAB 2D: Project Sequence Diagram"""
865
  path_obj = Path(folder_path)
866
 
@@ -919,7 +867,7 @@ def process_folder_sequence(folder_path: str, entry_method: str = None, enrich:
919
  except Exception as e:
920
  return f"❌ Error: {e}", None, gr.update(visible=True, value=f"❌ Failed"), ""
921
 
922
- def process_folder_sequence_multi(folder_path: str, enrich: bool = False, provider: str = "sambanova", progress=gr.Progress()):
923
  """TAB 6: Generate MULTIPLE sequence diagrams, one per module"""
924
  path_obj = Path(folder_path)
925
 
@@ -1009,7 +957,7 @@ def process_folder_sequence_multi(folder_path: str, enrich: bool = False, provid
1009
  return f"❌ Error: {e}\n\nDetails:\n{error_detail}", [], [], None, "", gr.update(visible=True, value=f"❌ Failed")
1010
 
1011
  # --- TAB 5: AI PROPOSAL ---
1012
- def process_proposal_zip(zip_path, provider: str = "sambanova", progress=gr.Progress()):
1013
  """AI-powered architecture refactoring proposal"""
1014
  if not zip_path:
1015
  return "⚠️ Please upload a ZIP file.", None, None, gr.update(visible=True, value="⚠️ No File")
@@ -1061,94 +1009,123 @@ def process_proposal_zip(zip_path, provider: str = "sambanova", progress=gr.Prog
1061
  # --- TAB 6: MODAL REFACTORING ---
1062
  def run_modal_refactoring_zip(zip_path, file_path, instruction, test_path=None, progress=gr.Progress()):
1063
  """Execute refactoring in Modal cloud sandbox"""
1064
- # Validation
1065
  if not zip_path:
1066
  return "⚠️ Please upload a ZIP file.", gr.update(visible=False)
1067
- if not file_path or file_path.startswith("❌") or file_path.startswith("⚠️"):
1068
  return "⚠️ Please select a valid Python file.", gr.update(visible=False)
1069
- if not instruction or not instruction.strip():
1070
  return "⚠️ Please provide refactoring instructions.", gr.update(visible=False)
1071
 
1072
- # Handle test file selection
1073
  if test_path == "None (Skip tests)":
1074
  test_path = None
1075
 
1076
- temp_extract = None
1077
  try:
1078
- temp_extract = tempfile.mkdtemp()
1079
 
1080
- progress(0.2, desc="πŸ“¦ Extracting project...")
1081
- with zipfile.ZipFile(zip_path, 'r') as zip_ref:
1082
- zip_ref.extractall(temp_extract)
1083
 
1084
- progress(0.4, desc="πŸ“„ Locating file...")
1085
 
1086
- # FIX: Search for file in extracted directory
1087
  target_file = None
1088
- for root, dirs, files in os.walk(temp_extract):
1089
- if file_path in root or file_path.endswith(os.path.basename(root)):
1090
- continue
1091
- potential_path = Path(root) / os.path.basename(file_path)
1092
- if potential_path.exists():
1093
- target_file = potential_path
1094
- break
1095
 
1096
- # If not found, try direct path
1097
- if not target_file:
1098
- target_file = Path(temp_extract) / file_path
 
 
 
 
 
 
 
 
 
 
 
1099
 
1100
- if not target_file or not target_file.exists():
1101
- # List what we actually have for debugging
1102
- all_py_files = []
1103
- for root, dirs, files in os.walk(temp_extract):
1104
  for f in files:
1105
  if f.endswith('.py'):
1106
- rel_path = Path(root).relative_to(temp_extract) / f
1107
- all_py_files.append(str(rel_path))
1108
 
1109
- return f"❌ File not found: {file_path}\n\nAvailable files:\n" + "\n".join(all_py_files[:10]), gr.update(visible=False)
1110
 
1111
- progress(0.5, desc="πŸ“– Reading file...")
1112
  original_code = target_file.read_text(encoding='utf-8')
 
1113
 
1114
- # Read test file
1115
- test_code = None
1116
- if test_path:
1117
- test_file = None
1118
- for root, dirs, files in os.walk(temp_extract):
1119
- potential_path = Path(root) / os.path.basename(test_path)
1120
- if potential_path.exists():
1121
- test_file = potential_path
1122
- break
1123
-
1124
- if test_file and test_file.exists():
1125
- test_code = test_file.read_text(encoding='utf-8')
1126
 
1127
- progress(0.7, desc="☁️ Sending to Modal...")
1128
 
1129
- # Call Modal
1130
  try:
1131
- from server import apply_refactoring_safely
 
 
 
 
 
 
 
 
 
1132
 
1133
- # Use just the filename for Modal
1134
- file_name = os.path.basename(file_path)
1135
- result = apply_refactoring_safely(file_name, instruction, test_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
1136
 
1137
  progress(1.0, desc="βœ… Complete!")
1138
- return result, gr.update(visible=False)
 
 
 
 
 
 
 
 
 
1139
 
1140
  except ImportError:
1141
- return "⚠️ Modal not configured.", gr.update(visible=False)
1142
  except Exception as modal_error:
1143
  logger.error(f"Modal error: {modal_error}")
1144
- return f"❌ Modal failed: {str(modal_error)}", gr.update(visible=False)
 
1145
 
1146
  except Exception as e:
1147
  logger.error(f"Refactoring error: {e}")
1148
- return f"❌ Error: {str(e)}", gr.update(visible=False)
 
1149
  finally:
1150
- safe_cleanup(temp_extract)
1151
- # --- CUSTOM CSS ---
 
1152
  custom_css = """
1153
  .gradio-container {
1154
  font-family: 'Inter', sans-serif;
@@ -1286,6 +1263,7 @@ with gr.Blocks(
1286
  with gr.Tabs():
1287
 
1288
  # TAB 1: Single File
 
1289
  with gr.Tab("πŸ“„ Single File Analysis", id=0):
1290
  gr.HTML('<div class="info-card"><strong>πŸ’‘ Smart Analysis:</strong> Paste Python code to detect design patterns, get recommendations, and see before/after UML visualizations.</div>')
1291
 
@@ -1310,7 +1288,7 @@ with gr.Blocks(
1310
  )
1311
  provider_choice = gr.Dropdown(
1312
  choices=["sambanova", "gemini", "nebius", "openai"],
1313
- value="sambanova",
1314
  label="LLM Provider",
1315
  scale=1
1316
  )
@@ -1322,14 +1300,60 @@ with gr.Blocks(
1322
  elem_classes=["primary-button"]
1323
  )
1324
 
1325
- # --- SECTION 2: READ ME / RECOMMENDATIONS ---
1326
- gr.Markdown("### πŸ“– Analysis & Recommendations")
1327
- with gr.Group(elem_classes=["output-card"]):
1328
- status_banner_1 = gr.Markdown(visible=False, elem_classes=["banner"])
1329
- pattern_report_single = gr.Markdown(
1330
- value="*Analysis report will appear here after clicking Analyze...*"
1331
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1332
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1333
  # --- SECTION 3: UML CODE & DIAGRAM ---
1334
  gr.Markdown("### 🎨 UML Visualization")
1335
  with gr.Group(elem_classes=["output-card"]):
@@ -1341,18 +1365,18 @@ with gr.Blocks(
1341
  with gr.Accordion("πŸ“ PlantUML Source Code", open=False):
1342
  text_output_1 = gr.Code(language="markdown", lines=10, label="UML Code")
1343
 
1344
- # --- HIDDEN SECTION: PATTERN VISUALIZATION (Appears on demand) ---
1345
- with gr.Row(visible=False) as pattern_uml_section_single:
1346
  gr.HTML('<div class="info-card" style="margin-top: 2rem;"><strong>πŸ’‘ Recommended Improvements:</strong> Visual comparison showing how design patterns can improve your code structure.</div>')
1347
 
1348
- with gr.Row(visible=False) as pattern_selector_section_single:
1349
  recommendation_dropdown_single = gr.Dropdown(
1350
  label="πŸ“‹ Select Recommendation to Visualize",
1351
  choices=[],
1352
  interactive=True
1353
  )
1354
 
1355
- with gr.Row(visible=False) as pattern_comparison_section_single:
1356
  with gr.Column(scale=1):
1357
  gr.Markdown("#### ⚠️ Before (Current Structure)")
1358
  with gr.Group(elem_classes=["output-card"]):
@@ -1375,6 +1399,16 @@ with gr.Blocks(
1375
  with gr.Accordion("πŸ“ PlantUML Code", open=False):
1376
  pattern_after_uml_single = gr.Code(language="markdown", lines=8)
1377
 
 
 
 
 
 
 
 
 
 
 
1378
  # Event handlers
1379
  analyze_btn.click(
1380
  fn=process_code_snippet_with_patterns,
@@ -1382,15 +1416,16 @@ with gr.Blocks(
1382
  outputs=[
1383
  pattern_report_single,
1384
  text_output_1,
 
1385
  img_output_1,
1386
  status_banner_1,
1387
  pattern_uml_section_single,
1388
- pattern_comparison_section_single,
1389
  recommendation_dropdown_single,
1390
- pattern_before_img_single,
1391
- pattern_after_img_single,
1392
- pattern_before_uml_single,
1393
- pattern_after_uml_single
1394
  ]
1395
  ).then(
1396
  fn=lambda x: x,
@@ -1404,66 +1439,11 @@ with gr.Blocks(
1404
  outputs=[pattern_before_img_single, pattern_after_img_single, pattern_before_uml_single, pattern_after_uml_single]
1405
  )
1406
 
1407
- # EXAMPLES
1408
- gr.Examples(
1409
- examples=[
1410
- ["""# Strategy Pattern Example
1411
- from abc import ABC, abstractmethod
1412
-
1413
- class PaymentStrategy(ABC):
1414
- @abstractmethod
1415
- def pay(self, amount):
1416
- pass
1417
-
1418
- class CreditCardPayment(PaymentStrategy):
1419
- def pay(self, amount):
1420
- return f"Paid ${amount} with Credit Card"
1421
-
1422
- class PayPalPayment(PaymentStrategy):
1423
- def pay(self, amount):
1424
- return f"Paid ${amount} with PayPal"
1425
-
1426
- class ShoppingCart:
1427
- def __init__(self, payment_strategy):
1428
- self.payment_strategy = payment_strategy
1429
-
1430
- def checkout(self, total):
1431
- return self.payment_strategy.pay(total)
1432
- """, False, "sambanova"],
1433
- ["""# Singleton Pattern Example
1434
- class Database:
1435
- _instance = None
1436
-
1437
- def __new__(cls):
1438
- if cls._instance is None:
1439
- cls._instance = super().__new__(cls)
1440
- cls._instance.connection = None
1441
- return cls._instance
1442
-
1443
- def connect(self):
1444
- if not self.connection:
1445
- self.connection = "Connected to DB"
1446
- return self.connection
1447
-
1448
- # Factory Pattern Example
1449
- class ProductFactory:
1450
- @staticmethod
1451
- def create_product(product_type):
1452
- if product_type == "book":
1453
- return Book()
1454
- elif product_type == "electronics":
1455
- return Electronics()
1456
- return None
1457
- """, False, "sambanova"],
1458
- ],
1459
- inputs=[code_input, enrich_checkbox, provider_choice],
1460
- label="πŸ“š Quick Examples - Click to Load"
1461
- )
1462
 
1463
  # TAB 2: Project Map
1464
  with gr.Tab("πŸ“‚ Project Map", id=1):
1465
  gr.HTML('<div class="info-card"><strong>πŸ—ΊοΈ Full Project Analysis:</strong> Upload a ZIP file to visualize all classes, relationships, and design patterns in your Python project. Works best with 5-50 files.</div>')
1466
-
1467
  # Store the project structure for pattern detection
1468
  stored_project_structure = gr.State(None)
1469
  stored_project_code = gr.State(None)
@@ -1496,9 +1476,17 @@ class ProductFactory:
1496
 
1497
  with gr.Accordion("πŸ“ PlantUML Source", open=False):
1498
  text_output_2 = gr.Code(language="markdown", lines=10)
1499
-
 
 
 
 
 
 
 
 
1500
  # Pattern Detection Section (appears after diagram generation)
1501
- with gr.Row(visible=False) as pattern_section:
1502
  with gr.Column(scale=1):
1503
  gr.HTML('<div class="info-card"><strong>πŸ›οΈ Design Pattern Analysis:</strong> Detect existing patterns and get AI-powered recommendations for architectural improvements.</div>')
1504
 
@@ -1510,7 +1498,7 @@ class ProductFactory:
1510
  )
1511
  pattern_provider_choice = gr.Dropdown(
1512
  choices=["sambanova", "gemini", "nebius", "openai"],
1513
- value="sambanova",
1514
  label="LLM Provider",
1515
  scale=1
1516
  )
@@ -1518,34 +1506,27 @@ class ProductFactory:
1518
  detect_patterns_btn = gr.Button(
1519
  "πŸ” Detect Patterns & Recommendations",
1520
  variant="secondary",
1521
- size="lg"
 
1522
  )
1523
 
1524
  with gr.Column(scale=2):
1525
  with gr.Group(elem_classes=["output-card"]):
1526
  pattern_status = gr.Markdown(visible=False, elem_classes=["banner"])
1527
 
1528
- # Pattern Report Output
1529
- with gr.Row(visible=False) as pattern_results_section:
1530
- with gr.Column():
1531
- with gr.Group(elem_classes=["output-card"]):
1532
- pattern_report_output = gr.Markdown(
1533
- label="πŸ“Š Pattern Analysis Report",
1534
- value="*Waiting for analysis...*"
1535
- )
1536
 
1537
  # Pattern UML Visualizations
1538
- with gr.Row(visible=False) as pattern_uml_section:
1539
  gr.HTML('<div class="info-card"><strong>πŸ’‘ Before & After:</strong> Visual comparison of current design vs. recommended pattern implementation.</div>')
1540
 
1541
- with gr.Row(visible=False) as pattern_selector_section:
1542
  recommendation_dropdown = gr.Dropdown(
1543
  label="πŸ“‹ Select Recommendation to Visualize",
1544
  choices=[],
1545
  interactive=True
1546
  )
1547
 
1548
- with gr.Row(visible=False) as pattern_comparison_section:
1549
  with gr.Column(scale=1):
1550
  gr.Markdown("#### ⚠️ Before (Current Structure)")
1551
  with gr.Group(elem_classes=["output-card"]):
@@ -1568,6 +1549,16 @@ class ProductFactory:
1568
  with gr.Accordion("πŸ“ PlantUML Code", open=False):
1569
  pattern_after_uml = gr.Code(language="markdown", lines=8)
1570
 
 
 
 
 
 
 
 
 
 
 
1571
  def process_pattern_detection_from_structure(structure, code, enrich: bool = True, provider: str = "openai", progress=gr.Progress()):
1572
  """
1573
  Analyze patterns using already-parsed structure and code.
@@ -1577,8 +1568,9 @@ class ProductFactory:
1577
  if not structure or not code:
1578
  logging.warning("⚠️ Pattern detection called without structure or code")
1579
  return (
1580
- "⚠️ Please generate the class diagram first.",
1581
- gr.update(visible=True, value="⚠️ No Data"),
 
1582
  gr.update(visible=False),
1583
  gr.update(visible=False),
1584
  gr.update(visible=False),
@@ -1836,7 +1828,8 @@ class ProductFactory:
1836
  pattern_after_img,
1837
  pattern_before_uml,
1838
  pattern_after_uml
1839
- ]
 
1840
  )
1841
  recommendation_dropdown.change(
1842
  fn=update_recommendation_visualization,
@@ -1844,15 +1837,7 @@ class ProductFactory:
1844
  outputs=[pattern_before_img, pattern_after_img, pattern_before_uml, pattern_after_uml]
1845
  )
1846
 
1847
- # EXAMPLES
1848
- gr.Examples(
1849
- examples=[
1850
- ["demo_ecommerce.zip"],
1851
- ["demo_fastapi.zip"],
1852
- ],
1853
- inputs=project_zip,
1854
- label="⚑ Demo Projects (Click to Load)"
1855
- )
1856
 
1857
  # TAB 3: MULTI-MODULE USE CASES
1858
 
@@ -1870,7 +1855,7 @@ class ProductFactory:
1870
  )
1871
 
1872
  with gr.Row():
1873
- multi_provider = gr.Dropdown(choices=["sambanova", "gemini", "nebius", "openai"], value="sambanova", label="LLM Provider")
1874
  multi_enrich = gr.Checkbox(label="✨ AI Enrichment", value=True, info="Better actor detection")
1875
 
1876
  multi_scan_btn = gr.Button("πŸ” Generate Module Diagrams", variant="primary", size="lg", elem_classes=["primary-button"])
@@ -1878,7 +1863,15 @@ class ProductFactory:
1878
  with gr.Column(scale=1):
1879
  multi_status_banner = gr.Markdown(visible=False, elem_classes=["banner"])
1880
  multi_summary = gr.Textbox(label="Generated Diagrams", lines=5, interactive=False)
1881
-
 
 
 
 
 
 
 
 
1882
  gr.Markdown("### πŸ“Š Diagrams by Module")
1883
 
1884
  multi_gallery = gr.State([])
@@ -1901,15 +1894,7 @@ class ProductFactory:
1901
  multi_scan_btn.click(fn=process_folder_usecase_multi_zip, inputs=[multi_zip_input, multi_enrich, multi_provider], outputs=[multi_summary, multi_gallery, multi_module_selector, multi_diagram_img, multi_diagram_puml, multi_status_banner])
1902
  multi_module_selector.change(fn=update_diagram_viewer, inputs=[multi_gallery, multi_module_selector], outputs=[multi_diagram_img, multi_diagram_puml])
1903
 
1904
- # EXAMPLES
1905
- gr.Examples(
1906
- examples=[
1907
- ["demo_ecommerce.zip"],
1908
- ["demo_fastapi.zip"],
1909
- ],
1910
- inputs=multi_zip_input,
1911
- label="⚑ Demo Projects (Click to Load)"
1912
- )
1913
 
1914
  # TAB 4: MULTI-MODULE SEQUENCES
1915
  with gr.Tab("🎬 Multi-Module Sequences", id=3):
@@ -1926,7 +1911,7 @@ class ProductFactory:
1926
  )
1927
 
1928
  with gr.Row():
1929
- seq_multi_provider = gr.Dropdown(choices=["sambanova", "gemini", "nebius", "openai"], value="sambanova", label="LLM Provider")
1930
  seq_multi_enrich = gr.Checkbox(label="✨ AI Enrichment", value=False, info="Slow but better names")
1931
 
1932
  seq_multi_scan_btn = gr.Button("πŸ” Generate Module Sequences", variant="primary", size="lg", elem_classes=["primary-button"])
@@ -1934,6 +1919,15 @@ class ProductFactory:
1934
  with gr.Column(scale=1):
1935
  seq_multi_status_banner = gr.Markdown(visible=False, elem_classes=["banner"])
1936
  seq_multi_summary = gr.Textbox(label="Generated Diagrams", lines=5, interactive=False)
 
 
 
 
 
 
 
 
 
1937
 
1938
  gr.Markdown("### 🎬 Sequence Diagrams by Module")
1939
 
@@ -1957,15 +1951,7 @@ class ProductFactory:
1957
  seq_multi_scan_btn.click(fn=process_folder_sequence_multi_zip, inputs=[seq_multi_zip_input, seq_multi_enrich, seq_multi_provider], outputs=[seq_multi_summary, seq_multi_gallery, seq_multi_module_selector, seq_multi_diagram_img, seq_multi_diagram_puml, seq_multi_status_banner])
1958
  seq_multi_module_selector.change(fn=update_seq_diagram_viewer, inputs=[seq_multi_gallery, seq_multi_module_selector], outputs=[seq_multi_diagram_img, seq_multi_diagram_puml])
1959
 
1960
- # EXAMPLES
1961
- gr.Examples(
1962
- examples=[
1963
- ["demo_ecommerce.zip"],
1964
- ["demo_fastapi.zip"],
1965
- ],
1966
- inputs=seq_multi_zip_input,
1967
- label="⚑ Demo Projects (Click to Load)"
1968
- )
1969
 
1970
 
1971
  # TAB 5: AI PROPOSAL
@@ -1981,37 +1967,36 @@ class ProductFactory:
1981
  # Add provider dropdown
1982
  proposal_provider = gr.Dropdown(
1983
  choices=["sambanova", "gemini", "nebius", "openai"],
1984
- value="sambanova",
1985
  label="LLM Provider",
1986
  info="Select AI provider for analysis"
1987
  )
1988
 
1989
  propose_btn = gr.Button("🧠 Generate Proposal", variant="primary", size="lg")
 
 
 
 
 
 
 
 
1990
  status_banner_3 = gr.Markdown(visible=False, elem_classes=["banner"])
1991
  proposal_output = gr.Code(language="json", label="πŸ“‹ Analysis", lines=15)
 
1992
 
1993
  with gr.Column():
1994
  gr.Markdown("#### πŸ“Š Results")
1995
  img_output_3 = gr.Image(label="🎨 Proposed Architecture", type="pil")
1996
  with gr.Accordion("πŸ“ Proposed PlantUML", open=False):
1997
  text_output_3 = gr.Code(language="markdown", lines=10)
 
1998
 
1999
  propose_btn.click(
2000
  fn=process_proposal_zip,
2001
  inputs=[proposal_zip, proposal_provider],
2002
  outputs=[proposal_output, text_output_3, img_output_3, status_banner_3]
2003
  )
2004
-
2005
- # EXAMPLES
2006
- gr.Examples(
2007
- examples=[
2008
- ["demo_ecommerce.zip"],
2009
- ["demo_fastapi.zip"],
2010
- ],
2011
- inputs=proposal_zip,
2012
- label="⚑ Demo Projects (Click to Load)"
2013
- )
2014
-
2015
  # TAB 6: Modal Refactoring
2016
  with gr.Tab("☁️ Safe Refactoring", id=5):
2017
  gr.Markdown("### Production-Safe Cloud Execution\nRefactor code in isolated Modal sandboxes with testing.")
@@ -2023,41 +2008,297 @@ class ProductFactory:
2023
  modal_zip = gr.File(label="πŸ“¦ Upload Project (ZIP)", file_types=[".zip"], type="filepath")
2024
  file_dropdown = gr.Dropdown(label="Target File", choices=[], interactive=True)
2025
  test_dropdown = gr.Dropdown(label="Test File (Optional)", choices=[], interactive=True)
 
2026
  instruction_input = gr.Textbox(
2027
  label="Refactoring Instructions",
2028
  placeholder="Extract Strategy pattern for payment methods...",
2029
  lines=5
2030
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2031
  execute_btn = gr.Button("πŸš€ Execute in Modal", variant="stop", size="lg")
2032
 
2033
  with gr.Column():
2034
  gr.Markdown("#### πŸ“Š Results")
2035
  modal_output = gr.Markdown(value="☁️ Waiting for execution...")
2036
- download_output = gr.File(label="πŸ“₯ Download", visible=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2037
 
2038
- # Auto-populate dropdowns on ZIP upload
2039
  modal_zip.change(
2040
  fn=extract_file_list,
2041
  inputs=modal_zip,
2042
  outputs=[file_dropdown, test_dropdown]
2043
  )
2044
 
 
2045
  execute_btn.click(
2046
- fn=run_modal_refactoring_zip,
2047
  inputs=[modal_zip, file_dropdown, instruction_input, test_dropdown],
2048
- outputs=[modal_output, download_output]
 
2049
  )
2050
 
2051
- # EXAMPLES
2052
- gr.Examples(
2053
- examples=[
2054
- ["demo_ecommerce.zip"],
2055
- ["demo_fastapi.zip"],
2056
- ],
2057
- inputs=modal_zip,
2058
- label="⚑ Demo Projects (Click to Load)"
2059
- )
 
 
 
 
2060
 
 
 
2061
  # FOOTER
2062
  gr.HTML("""
2063
  <div class="footer">
 
12
  from PIL import Image
13
  from plantuml import PlantUML
14
 
15
+ from a import LLMClientSingleton
16
  from services.pattern_detector import PatternDetectionService, PatternRecommendation
17
  from services.sequence_service import CallGraphVisitor, ProjectSequenceAnalyzer, SequenceDiagramService
18
  from services.usecase_service import UseCaseDiagramService
 
41
  PLANTUML_SERVER_URL = 'http://www.plantuml.com/plantuml/img/'
42
  plantuml_client = PlantUML(url=PLANTUML_SERVER_URL)
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  # Global singleton instance
46
  _llm_singleton = LLMClientSingleton()
 
120
  )
121
 
122
  # --- TAB 1: SINGLE FILE ANALYSIS ---
123
+ def process_code_snippet_with_patterns(code_snippet: str, enrich_types: bool = False, provider: str = "nebius"):
124
  """
125
  Analyze single Python code snippet:
126
  1. Detect design patterns and recommendations
 
397
 
398
  # -- TAB 3 : USE CASE DIAGRAM ---
399
 
400
+ def process_usecase_snippet(code_snippet: str, enrich: bool = True, provider: str = "nebius"):
401
  """TAB 1B: Single File Use Case Diagram"""
402
  if not code_snippet.strip():
403
  return "⚠️ Please enter some code.", None, gr.update(visible=False)
 
416
  except Exception as e:
417
  return f"❌ Error: {e}", None, gr.update(visible=True, value=f"❌ Error")
418
 
419
+ def process_folder_usecase(folder_path: str, enrich: bool = True, provider: str = "nebius", progress=gr.Progress()):
420
  """TAB 2B: Project Use Case Diagram - SINGLE COMBINED"""
421
  path_obj = Path(folder_path)
422
 
 
462
  except Exception as e:
463
  return f"❌ Error: {e}", None, gr.update(visible=True, value=f"❌ Failed")
464
 
465
+ def process_folder_usecase_multi(folder_path: str, enrich: bool = True, provider: str = "nebius", progress=gr.Progress()):
466
  """TAB 3: Project Use Case Diagrams - MULTIPLE BY MODULE"""
467
  path_obj = Path(folder_path)
468
 
 
550
  logging.error(f"Multi-diagram error: {error_detail}")
551
  return f"❌ Error: {e}\n\nDetails:\n{error_detail}", [], [], None, "", gr.update(visible=True, value=f"❌ Failed")
552
 
553
+ def process_folder_usecase_multi_zip(zip_path, enrich: bool = True, provider: str = "nebius", progress=gr.Progress()):
554
  """TAB 3: Multi-Module Use Cases from ZIP file"""
555
 
556
  # βœ… FIX: Check if zip_path is provided and is a valid file
 
620
  if "error" in diagrams_dict:
621
  return diagrams_dict["error"], [], [], None, "", gr.update(visible=True, value="❌ Failed")
622
 
623
+ progress(0.8, desc="Rendering diagrams...")
624
 
625
  diagram_outputs = []
626
 
 
665
  finally:
666
  safe_cleanup(temp_dir)
667
 
 
 
668
  # --- TAB 4: sequence diagrams ---
669
+ def process_folder_sequence_multi_zip(zip_path, enrich: bool = False, provider: str = "nebius", progress=gr.Progress()):
670
  """TAB 4: Multi-Module Sequences from ZIP file"""
671
 
672
  # βœ… FIX: Check if zip_path is provided and is a valid file
 
782
  finally:
783
  safe_cleanup(temp_dir)
784
 
785
+ def process_sequence_snippet(code_snippet: str, entry_method: str = None, enrich: bool = True, provider: str = "nebius"):
786
  """TAB 1C: Single File Sequence Diagram"""
787
  if not code_snippet.strip():
788
  return "⚠️ Please enter some code.", None, gr.update(visible=False), ""
 
808
  except Exception as e:
809
  return f"❌ Error: {e}", None, gr.update(visible=True, value=f"❌ Error"), ""
810
 
811
+ def process_folder_sequence(folder_path: str, entry_method: str = None, enrich: bool = False, provider: str = "nebius", progress=gr.Progress()):
812
  """TAB 2D: Project Sequence Diagram"""
813
  path_obj = Path(folder_path)
814
 
 
867
  except Exception as e:
868
  return f"❌ Error: {e}", None, gr.update(visible=True, value=f"❌ Failed"), ""
869
 
870
+ def process_folder_sequence_multi(folder_path: str, enrich: bool = False, provider: str = "nebius", progress=gr.Progress()):
871
  """TAB 6: Generate MULTIPLE sequence diagrams, one per module"""
872
  path_obj = Path(folder_path)
873
 
 
957
  return f"❌ Error: {e}\n\nDetails:\n{error_detail}", [], [], None, "", gr.update(visible=True, value=f"❌ Failed")
958
 
959
  # --- TAB 5: AI PROPOSAL ---
960
+ def process_proposal_zip(zip_path, provider: str = "nebius", progress=gr.Progress()):
961
  """AI-powered architecture refactoring proposal"""
962
  if not zip_path:
963
  return "⚠️ Please upload a ZIP file.", None, None, gr.update(visible=True, value="⚠️ No File")
 
1009
  # --- TAB 6: MODAL REFACTORING ---
1010
  def run_modal_refactoring_zip(zip_path, file_path, instruction, test_path=None, progress=gr.Progress()):
1011
  """Execute refactoring in Modal cloud sandbox"""
1012
+
1013
  if not zip_path:
1014
  return "⚠️ Please upload a ZIP file.", gr.update(visible=False)
1015
+ if not file_path or file_path.startswith(("❌", "⚠️")):
1016
  return "⚠️ Please select a valid Python file.", gr.update(visible=False)
1017
+ if not instruction.strip():
1018
  return "⚠️ Please provide refactoring instructions.", gr.update(visible=False)
1019
 
 
1020
  if test_path == "None (Skip tests)":
1021
  test_path = None
1022
 
1023
+ temp_dir = None
1024
  try:
1025
+ temp_dir = tempfile.mkdtemp()
1026
 
1027
+ progress(0.2, desc="πŸ“¦ Extracting ZIP...")
1028
+ with zipfile.ZipFile(zip_path, 'r') as z:
1029
+ z.extractall(temp_dir)
1030
 
1031
+ progress(0.4, desc="πŸ“„ Reading files...")
1032
 
1033
+ # ============ FIND FILES IN EXTRACTED ZIP ============
1034
  target_file = None
1035
+ test_file = None
 
 
 
 
 
 
1036
 
1037
+ for root, dirs, files in os.walk(temp_dir):
1038
+ for f in files:
1039
+ if not f.endswith('.py'):
1040
+ continue
1041
+
1042
+ full_path = Path(root) / f
1043
+ rel_path = full_path.relative_to(temp_dir)
1044
+
1045
+ # Match by relative path (handles nested folders)
1046
+ if str(rel_path) == file_path or str(rel_path).replace('\\', '/') == file_path.replace('\\', '/'):
1047
+ target_file = full_path
1048
+
1049
+ if test_path and (str(rel_path) == test_path or str(rel_path).replace('\\', '/') == test_path.replace('\\', '/')):
1050
+ test_file = full_path
1051
 
1052
+ if not target_file:
1053
+ all_py = []
1054
+ for root, dirs, files in os.walk(temp_dir):
 
1055
  for f in files:
1056
  if f.endswith('.py'):
1057
+ rel = Path(root).relative_to(temp_dir) / f
1058
+ all_py.append(str(rel))
1059
 
1060
+ return f"❌ File not found: {file_path}\n\nπŸ“‚ Available files:\n" + "\n".join(all_py[:15]), gr.update(visible=False)
1061
 
1062
+ # ============ READ CODE ============
1063
  original_code = target_file.read_text(encoding='utf-8')
1064
+ test_code = test_file.read_text(encoding='utf-8') if test_file else None
1065
 
1066
+ logger.info(f"βœ“ Found target: {target_file.name} ({len(original_code)} chars)")
1067
+ if test_file:
1068
+ logger.info(f"βœ“ Found test: {test_file.name} ({len(test_code)} chars)")
 
 
 
 
 
 
 
 
 
1069
 
1070
+ progress(0.6, desc="☁️ Sending to Modal...")
1071
 
1072
+ # ============ CALL MODAL WITH CODE CONTENT ============
1073
  try:
1074
+ # Import Modal function directly
1075
+ import modal
1076
+ modal_fn = modal.Function.from_name("architect-ai-surgeon", "safe_refactor_and_test")
1077
+
1078
+ # Call with CODE STRINGS (not paths!)
1079
+ result = modal_fn.remote(
1080
+ original_code,
1081
+ instruction,
1082
+ test_code
1083
+ )
1084
 
1085
+ # ============ CHECK RESULTS ============
1086
+ if not result["success"]:
1087
+ progress(1.0, desc="❌ Failed")
1088
+ return f"❌ Refactoring failed:\n{result['error']}", gr.update(visible=False)
1089
+
1090
+ if not result["test_results"]["passed"]:
1091
+ progress(1.0, desc="⚠️ Tests failed")
1092
+ return f"""⚠️ Code refactored but tests FAILED:
1093
+
1094
+ {result['test_results']['output']}
1095
+
1096
+ Code was NOT saved. Fix tests first.
1097
+ """, gr.update(visible=False)
1098
+
1099
+ # ============ SAVE REFACTORED CODE ============
1100
+ target_file.write_text(result["new_code"], encoding='utf-8')
1101
 
1102
  progress(1.0, desc="βœ… Complete!")
1103
+
1104
+ return f"""βœ… Refactoring completed successfully!
1105
+
1106
+ πŸ“Š Tests: PASSED βœ“
1107
+ πŸ’Ύ File: {file_path}
1108
+ πŸ“ Code: {len(result['new_code'])} chars
1109
+
1110
+ πŸ§ͺ Test output:
1111
+ {result['test_results']['output'][:500]}
1112
+ """, gr.update(visible=False)
1113
 
1114
  except ImportError:
1115
+ return "⚠️ Modal not installed. Run: pip install modal", gr.update(visible=False)
1116
  except Exception as modal_error:
1117
  logger.error(f"Modal error: {modal_error}")
1118
+ import traceback
1119
+ return f"❌ Modal failed: {modal_error}\n\n{traceback.format_exc()[:500]}", gr.update(visible=False)
1120
 
1121
  except Exception as e:
1122
  logger.error(f"Refactoring error: {e}")
1123
+ import traceback
1124
+ return f"❌ Error: {e}\n\n{traceback.format_exc()[:500]}", gr.update(visible=False)
1125
  finally:
1126
+ safe_cleanup(temp_dir)
1127
+
1128
+ # --- CUSTOM CSS ---
1129
  custom_css = """
1130
  .gradio-container {
1131
  font-family: 'Inter', sans-serif;
 
1263
  with gr.Tabs():
1264
 
1265
  # TAB 1: Single File
1266
+
1267
  with gr.Tab("πŸ“„ Single File Analysis", id=0):
1268
  gr.HTML('<div class="info-card"><strong>πŸ’‘ Smart Analysis:</strong> Paste Python code to detect design patterns, get recommendations, and see before/after UML visualizations.</div>')
1269
 
 
1288
  )
1289
  provider_choice = gr.Dropdown(
1290
  choices=["sambanova", "gemini", "nebius", "openai"],
1291
+ value="nebius",
1292
  label="LLM Provider",
1293
  scale=1
1294
  )
 
1300
  elem_classes=["primary-button"]
1301
  )
1302
 
1303
+ gr.Examples(
1304
+ examples=[
1305
+ ["""# Strategy Pattern Example
1306
+ from abc import ABC, abstractmethod
1307
+
1308
+ class PaymentStrategy(ABC):
1309
+ @abstractmethod
1310
+ def pay(self, amount):
1311
+ pass
1312
+
1313
+ class CreditCardPayment(PaymentStrategy):
1314
+ def pay(self, amount):
1315
+ return f"Paid ${amount} with Credit Card"
1316
+
1317
+ class PayPalPayment(PaymentStrategy):
1318
+ def pay(self, amount):
1319
+ return f"Paid ${amount} with PayPal"
1320
+
1321
+ class ShoppingCart:
1322
+ def __init__(self, payment_strategy):
1323
+ self.payment_strategy = payment_strategy
1324
+
1325
+ def checkout(self, total):
1326
+ return self.payment_strategy.pay(total)
1327
+ """, False, "openai"],
1328
+ ["""# Singleton Pattern Example
1329
+ class Database:
1330
+ _instance = None
1331
+
1332
+ def __new__(cls):
1333
+ if cls._instance is None:
1334
+ cls._instance = super().__new__(cls)
1335
+ cls._instance.connection = None
1336
+ return cls._instance
1337
+
1338
+ def connect(self):
1339
+ if not self.connection:
1340
+ self.connection = "Connected to DB"
1341
+ return self.connection
1342
 
1343
+ # Factory Pattern Example
1344
+ class ProductFactory:
1345
+ @staticmethod
1346
+ def create_product(product_type):
1347
+ if product_type == "book":
1348
+ return Book()
1349
+ elif product_type == "electronics":
1350
+ return Electronics()
1351
+ return None
1352
+ """, True, "nebius"],
1353
+ ],
1354
+ inputs=[code_input, enrich_checkbox, provider_choice],
1355
+ label="πŸ“š Quick Examples - Click to Load"
1356
+ )
1357
  # --- SECTION 3: UML CODE & DIAGRAM ---
1358
  gr.Markdown("### 🎨 UML Visualization")
1359
  with gr.Group(elem_classes=["output-card"]):
 
1365
  with gr.Accordion("πŸ“ PlantUML Source Code", open=False):
1366
  text_output_1 = gr.Code(language="markdown", lines=10, label="UML Code")
1367
 
1368
+ # --- SECTION: PATTERN VISUALIZATION (Appears on demand) ---
1369
+ with gr.Row(visible=True) as pattern_uml_section_single:
1370
  gr.HTML('<div class="info-card" style="margin-top: 2rem;"><strong>πŸ’‘ Recommended Improvements:</strong> Visual comparison showing how design patterns can improve your code structure.</div>')
1371
 
1372
+ with gr.Row(visible=True) as pattern_selector_section_single:
1373
  recommendation_dropdown_single = gr.Dropdown(
1374
  label="πŸ“‹ Select Recommendation to Visualize",
1375
  choices=[],
1376
  interactive=True
1377
  )
1378
 
1379
+ with gr.Row(visible=True) as pattern_comparison_section_single:
1380
  with gr.Column(scale=1):
1381
  gr.Markdown("#### ⚠️ Before (Current Structure)")
1382
  with gr.Group(elem_classes=["output-card"]):
 
1399
  with gr.Accordion("πŸ“ PlantUML Code", open=False):
1400
  pattern_after_uml_single = gr.Code(language="markdown", lines=8)
1401
 
1402
+
1403
+ # --- SECTION 3: READ ME / RECOMMENDATIONS ---
1404
+ gr.Markdown("### πŸ“– Analysis & Recommendations")
1405
+ with gr.Group(elem_classes=["output-card"]):
1406
+ status_banner_1 = gr.Markdown(visible=False, elem_classes=["banner"])
1407
+ pattern_report_single = gr.Markdown(
1408
+ value="*Analysis report will appear here after clicking Analyze...*"
1409
+ )
1410
+
1411
+
1412
  # Event handlers
1413
  analyze_btn.click(
1414
  fn=process_code_snippet_with_patterns,
 
1416
  outputs=[
1417
  pattern_report_single,
1418
  text_output_1,
1419
+
1420
  img_output_1,
1421
  status_banner_1,
1422
  pattern_uml_section_single,
1423
+ pattern_comparison_section_single, # ← Controls visibility
1424
  recommendation_dropdown_single,
1425
+ pattern_before_img_single, # ← Updates "Before" image
1426
+ pattern_after_img_single, # ← Updates "After" image
1427
+ pattern_before_uml_single, # ← Updates "Before" UML code
1428
+ pattern_after_uml_single # ← Updates "After" UML code
1429
  ]
1430
  ).then(
1431
  fn=lambda x: x,
 
1439
  outputs=[pattern_before_img_single, pattern_after_img_single, pattern_before_uml_single, pattern_after_uml_single]
1440
  )
1441
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1442
 
1443
  # TAB 2: Project Map
1444
  with gr.Tab("πŸ“‚ Project Map", id=1):
1445
  gr.HTML('<div class="info-card"><strong>πŸ—ΊοΈ Full Project Analysis:</strong> Upload a ZIP file to visualize all classes, relationships, and design patterns in your Python project. Works best with 5-50 files.</div>')
1446
+
1447
  # Store the project structure for pattern detection
1448
  stored_project_structure = gr.State(None)
1449
  stored_project_code = gr.State(None)
 
1476
 
1477
  with gr.Accordion("πŸ“ PlantUML Source", open=False):
1478
  text_output_2 = gr.Code(language="markdown", lines=10)
1479
+ # EXAMPLES
1480
+ gr.Examples(
1481
+ examples=[
1482
+ ["demo_ecommerce.zip"],
1483
+ ["demo_fastapi.zip"],
1484
+ ],
1485
+ inputs=project_zip,
1486
+ label="⚑ Demo Projects (Click to Load)"
1487
+ )
1488
  # Pattern Detection Section (appears after diagram generation)
1489
+ with gr.Row(visible=True) as pattern_section:
1490
  with gr.Column(scale=1):
1491
  gr.HTML('<div class="info-card"><strong>πŸ›οΈ Design Pattern Analysis:</strong> Detect existing patterns and get AI-powered recommendations for architectural improvements.</div>')
1492
 
 
1498
  )
1499
  pattern_provider_choice = gr.Dropdown(
1500
  choices=["sambanova", "gemini", "nebius", "openai"],
1501
+ value="nebius",
1502
  label="LLM Provider",
1503
  scale=1
1504
  )
 
1506
  detect_patterns_btn = gr.Button(
1507
  "πŸ” Detect Patterns & Recommendations",
1508
  variant="secondary",
1509
+ size="lg",
1510
+ elem_id="detect_patterns_btn"
1511
  )
1512
 
1513
  with gr.Column(scale=2):
1514
  with gr.Group(elem_classes=["output-card"]):
1515
  pattern_status = gr.Markdown(visible=False, elem_classes=["banner"])
1516
 
 
 
 
 
 
 
 
 
1517
 
1518
  # Pattern UML Visualizations
1519
+ with gr.Row(visible=True) as pattern_uml_section:
1520
  gr.HTML('<div class="info-card"><strong>πŸ’‘ Before & After:</strong> Visual comparison of current design vs. recommended pattern implementation.</div>')
1521
 
1522
+ with gr.Row(visible=True) as pattern_selector_section:
1523
  recommendation_dropdown = gr.Dropdown(
1524
  label="πŸ“‹ Select Recommendation to Visualize",
1525
  choices=[],
1526
  interactive=True
1527
  )
1528
 
1529
+ with gr.Row(visible=True) as pattern_comparison_section:
1530
  with gr.Column(scale=1):
1531
  gr.Markdown("#### ⚠️ Before (Current Structure)")
1532
  with gr.Group(elem_classes=["output-card"]):
 
1549
  with gr.Accordion("πŸ“ PlantUML Code", open=False):
1550
  pattern_after_uml = gr.Code(language="markdown", lines=8)
1551
 
1552
+ # Pattern Report Output
1553
+ with gr.Row(visible=True) as pattern_results_section:
1554
+ with gr.Column():
1555
+ with gr.Group(elem_classes=["output-card"]):
1556
+ pattern_report_output = gr.Markdown(
1557
+ label="πŸ“Š Pattern Analysis Report",
1558
+ value="*Waiting for analysis...*"
1559
+ )
1560
+
1561
+
1562
  def process_pattern_detection_from_structure(structure, code, enrich: bool = True, provider: str = "openai", progress=gr.Progress()):
1563
  """
1564
  Analyze patterns using already-parsed structure and code.
 
1568
  if not structure or not code:
1569
  logging.warning("⚠️ Pattern detection called without structure or code")
1570
  return (
1571
+ "⚠️ **Please generate the class diagram first.**\n\n1. Click an example below (demo_ecommerce.zip)\n2. Click 'πŸ” Scan Project'\n3. Then try pattern detection again",
1572
+ gr.update(visible=True, value="⚠️ No Data - Scan Project First"),
1573
+ gr.update(visible=True), # Show the warning message
1574
  gr.update(visible=False),
1575
  gr.update(visible=False),
1576
  gr.update(visible=False),
 
1828
  pattern_after_img,
1829
  pattern_before_uml,
1830
  pattern_after_uml
1831
+ ],
1832
+ show_progress="full"
1833
  )
1834
  recommendation_dropdown.change(
1835
  fn=update_recommendation_visualization,
 
1837
  outputs=[pattern_before_img, pattern_after_img, pattern_before_uml, pattern_after_uml]
1838
  )
1839
 
1840
+
 
 
 
 
 
 
 
 
1841
 
1842
  # TAB 3: MULTI-MODULE USE CASES
1843
 
 
1855
  )
1856
 
1857
  with gr.Row():
1858
+ multi_provider = gr.Dropdown(choices=["sambanova", "gemini", "nebius", "openai"], value="nebius", label="LLM Provider")
1859
  multi_enrich = gr.Checkbox(label="✨ AI Enrichment", value=True, info="Better actor detection")
1860
 
1861
  multi_scan_btn = gr.Button("πŸ” Generate Module Diagrams", variant="primary", size="lg", elem_classes=["primary-button"])
 
1863
  with gr.Column(scale=1):
1864
  multi_status_banner = gr.Markdown(visible=False, elem_classes=["banner"])
1865
  multi_summary = gr.Textbox(label="Generated Diagrams", lines=5, interactive=False)
1866
+ # EXAMPLES
1867
+ gr.Examples(
1868
+ examples=[
1869
+ ["demo_ecommerce.zip"],
1870
+ ["demo_fastapi.zip"],
1871
+ ],
1872
+ inputs=multi_zip_input,
1873
+ label="⚑ Demo Projects (Click to Load)"
1874
+ )
1875
  gr.Markdown("### πŸ“Š Diagrams by Module")
1876
 
1877
  multi_gallery = gr.State([])
 
1894
  multi_scan_btn.click(fn=process_folder_usecase_multi_zip, inputs=[multi_zip_input, multi_enrich, multi_provider], outputs=[multi_summary, multi_gallery, multi_module_selector, multi_diagram_img, multi_diagram_puml, multi_status_banner])
1895
  multi_module_selector.change(fn=update_diagram_viewer, inputs=[multi_gallery, multi_module_selector], outputs=[multi_diagram_img, multi_diagram_puml])
1896
 
1897
+
 
 
 
 
 
 
 
 
1898
 
1899
  # TAB 4: MULTI-MODULE SEQUENCES
1900
  with gr.Tab("🎬 Multi-Module Sequences", id=3):
 
1911
  )
1912
 
1913
  with gr.Row():
1914
+ seq_multi_provider = gr.Dropdown(choices=["sambanova", "gemini", "nebius", "openai"], value="nebius", label="LLM Provider")
1915
  seq_multi_enrich = gr.Checkbox(label="✨ AI Enrichment", value=False, info="Slow but better names")
1916
 
1917
  seq_multi_scan_btn = gr.Button("πŸ” Generate Module Sequences", variant="primary", size="lg", elem_classes=["primary-button"])
 
1919
  with gr.Column(scale=1):
1920
  seq_multi_status_banner = gr.Markdown(visible=False, elem_classes=["banner"])
1921
  seq_multi_summary = gr.Textbox(label="Generated Diagrams", lines=5, interactive=False)
1922
+ # EXAMPLES
1923
+ gr.Examples(
1924
+ examples=[
1925
+ ["demo_ecommerce.zip"],
1926
+ ["demo_fastapi.zip"],
1927
+ ],
1928
+ inputs=seq_multi_zip_input,
1929
+ label="⚑ Demo Projects (Click to Load)"
1930
+ )
1931
 
1932
  gr.Markdown("### 🎬 Sequence Diagrams by Module")
1933
 
 
1951
  seq_multi_scan_btn.click(fn=process_folder_sequence_multi_zip, inputs=[seq_multi_zip_input, seq_multi_enrich, seq_multi_provider], outputs=[seq_multi_summary, seq_multi_gallery, seq_multi_module_selector, seq_multi_diagram_img, seq_multi_diagram_puml, seq_multi_status_banner])
1952
  seq_multi_module_selector.change(fn=update_seq_diagram_viewer, inputs=[seq_multi_gallery, seq_multi_module_selector], outputs=[seq_multi_diagram_img, seq_multi_diagram_puml])
1953
 
1954
+
 
 
 
 
 
 
 
 
1955
 
1956
 
1957
  # TAB 5: AI PROPOSAL
 
1967
  # Add provider dropdown
1968
  proposal_provider = gr.Dropdown(
1969
  choices=["sambanova", "gemini", "nebius", "openai"],
1970
+ value="nebius",
1971
  label="LLM Provider",
1972
  info="Select AI provider for analysis"
1973
  )
1974
 
1975
  propose_btn = gr.Button("🧠 Generate Proposal", variant="primary", size="lg")
1976
+ gr.Examples(
1977
+ examples=[
1978
+ ["demo_ecommerce.zip"],
1979
+ ["demo_fastapi.zip"],
1980
+ ],
1981
+ inputs=proposal_zip,
1982
+ label="⚑ Demo Projects (Click to Load)"
1983
+ )
1984
  status_banner_3 = gr.Markdown(visible=False, elem_classes=["banner"])
1985
  proposal_output = gr.Code(language="json", label="πŸ“‹ Analysis", lines=15)
1986
+
1987
 
1988
  with gr.Column():
1989
  gr.Markdown("#### πŸ“Š Results")
1990
  img_output_3 = gr.Image(label="🎨 Proposed Architecture", type="pil")
1991
  with gr.Accordion("πŸ“ Proposed PlantUML", open=False):
1992
  text_output_3 = gr.Code(language="markdown", lines=10)
1993
+
1994
 
1995
  propose_btn.click(
1996
  fn=process_proposal_zip,
1997
  inputs=[proposal_zip, proposal_provider],
1998
  outputs=[proposal_output, text_output_3, img_output_3, status_banner_3]
1999
  )
 
 
 
 
 
 
 
 
 
 
 
2000
  # TAB 6: Modal Refactoring
2001
  with gr.Tab("☁️ Safe Refactoring", id=5):
2002
  gr.Markdown("### Production-Safe Cloud Execution\nRefactor code in isolated Modal sandboxes with testing.")
 
2008
  modal_zip = gr.File(label="πŸ“¦ Upload Project (ZIP)", file_types=[".zip"], type="filepath")
2009
  file_dropdown = gr.Dropdown(label="Target File", choices=[], interactive=True)
2010
  test_dropdown = gr.Dropdown(label="Test File (Optional)", choices=[], interactive=True)
2011
+
2012
  instruction_input = gr.Textbox(
2013
  label="Refactoring Instructions",
2014
  placeholder="Extract Strategy pattern for payment methods...",
2015
  lines=5
2016
  )
2017
+ # EXAMPLES
2018
+ gr.Examples(
2019
+ examples=[
2020
+ ["demo_ecommerce.zip"],
2021
+ ["demo_fastapi.zip"],
2022
+ ],
2023
+ inputs=modal_zip,
2024
+ label="⚑ Demo Projects (Click to Load)"
2025
+ )
2026
+
2027
+ # ============ PREDEFINED PROMPTS ============
2028
+ gr.Markdown("### πŸ“ Quick Prompts (Click to Use)")
2029
+
2030
+ with gr.Row():
2031
+ with gr.Column(scale=1):
2032
+ gr.Markdown("**πŸ›οΈ Design Patterns**")
2033
+ prompt_strategy = gr.Button("Strategy Pattern", size="sm", variant="secondary")
2034
+ prompt_factory = gr.Button("Factory Pattern", size="sm", variant="secondary")
2035
+ prompt_singleton = gr.Button("Singleton Pattern", size="sm", variant="secondary")
2036
+ prompt_observer = gr.Button("Observer Pattern", size="sm", variant="secondary")
2037
+
2038
+ with gr.Column(scale=1):
2039
+ gr.Markdown("**🧹 Code Quality**")
2040
+ prompt_docstrings = gr.Button("Add Docstrings", size="sm", variant="secondary")
2041
+ prompt_typing = gr.Button("Add Type Hints", size="sm", variant="secondary")
2042
+ prompt_error = gr.Button("Improve Error Handling", size="sm", variant="secondary")
2043
+ prompt_logging = gr.Button("Add Logging", size="sm", variant="secondary")
2044
+
2045
+ with gr.Column(scale=1):
2046
+ gr.Markdown("**⚑ Performance**")
2047
+ prompt_async = gr.Button("Convert to Async", size="sm", variant="secondary")
2048
+ prompt_cache = gr.Button("Add Caching", size="sm", variant="secondary")
2049
+ prompt_optimize = gr.Button("Optimize Loops", size="sm", variant="secondary")
2050
+ prompt_lazy = gr.Button("Add Lazy Loading", size="sm", variant="secondary")
2051
+
2052
  execute_btn = gr.Button("πŸš€ Execute in Modal", variant="stop", size="lg")
2053
 
2054
  with gr.Column():
2055
  gr.Markdown("#### πŸ“Š Results")
2056
  modal_output = gr.Markdown(value="☁️ Waiting for execution...")
2057
+
2058
+ # ============ ADD DOWNLOAD SECTION ============
2059
+ with gr.Group(visible=True) as download_section:
2060
+ gr.Markdown("### πŸ“₯ Download Refactored Code")
2061
+ refactored_code_preview = gr.Code(
2062
+ label="Preview (First 50 lines)",
2063
+ language="python",
2064
+ lines=20,
2065
+ interactive=False
2066
+ )
2067
+ download_file = gr.File(label="πŸ’Ύ Download Full File", visible=True)
2068
+
2069
+ # ============ PROMPT DEFINITIONS ============
2070
+ PROMPTS = {
2071
+ "strategy": """Refactor to use Strategy Pattern:
2072
+
2073
+ 1. Extract conditional logic into separate strategy classes
2074
+ 2. Create a base Strategy interface (ABC)
2075
+ 3. Implement concrete strategy classes
2076
+ 4. Use dependency injection for the strategy
2077
+ 5. Keep existing public API unchanged
2078
+
2079
+ Example structure:
2080
+ - BaseStrategy (ABC with execute method)
2081
+ - ConcreteStrategyA, ConcreteStrategyB classes
2082
+ - Context class that uses the strategy""",
2083
+
2084
+ "factory": """Refactor to use Factory Pattern:
2085
+
2086
+ 1. Create a Factory class to handle object creation
2087
+ 2. Move instantiation logic out of client code
2088
+ 3. Support multiple product types
2089
+ 4. Use a registry pattern if needed
2090
+ 5. Add validation for product types
2091
+
2092
+ Example structure:
2093
+ - ProductFactory class with create() method
2094
+ - Product interface/base class
2095
+ - Concrete product classes""",
2096
+
2097
+ "singleton": """Refactor to use Singleton Pattern:
2098
+
2099
+ 1. Make __new__ return the same instance
2100
+ 2. Add _instance class variable
2101
+ 3. Thread-safe implementation with Lock if needed
2102
+ 4. Preserve existing public methods
2103
+ 5. Add reset() method for testing""",
2104
+
2105
+ "observer": """Refactor to use Observer Pattern:
2106
+
2107
+ 1. Create Subject class to manage observers
2108
+ 2. Create Observer interface (ABC)
2109
+ 3. Implement notify mechanism
2110
+ 4. Add subscribe/unsubscribe methods
2111
+ 5. Support multiple observers""",
2112
+
2113
+ "docstrings": """Add comprehensive docstrings to all classes and methods using Google-style format with examples.""",
2114
+
2115
+ "typing": """Add Python type hints throughout the code using typing module (List, Dict, Optional, Union, Protocol).""",
2116
+
2117
+ "error": """Improve error handling with try-except blocks, custom exceptions, meaningful messages, and proper logging.""",
2118
+
2119
+ "logging": """Add comprehensive logging with INFO for operations, DEBUG for flow, ERROR for exceptions.""",
2120
+
2121
+ "async": """Convert synchronous code to async/await using asyncio, aiohttp, and proper async patterns.""",
2122
+
2123
+ "cache": """Add caching mechanism using functools.lru_cache, TTL, and cache invalidation strategies.""",
2124
+
2125
+ "optimize": """Optimize loops using list comprehensions, generators, enumerate, zip, and numpy where applicable.""",
2126
+
2127
+ "lazy": """Add lazy loading pattern using @property, lazy initialization, and on-demand resource loading."""
2128
+ }
2129
+
2130
+ # Store refactored code
2131
+ refactored_code_state = gr.State("")
2132
+ original_filename_state = gr.State("")
2133
+
2134
+ # ============ IMPROVED EXECUTION FUNCTION ============
2135
+ def run_modal_refactoring_with_download(zip_path, file_path, instruction, test_path=None, progress=gr.Progress()):
2136
+ """Execute refactoring and prepare download"""
2137
+
2138
+ if not zip_path:
2139
+ return "⚠️ Please upload a ZIP file.", gr.update(visible=False), "", "", ""
2140
+ if not file_path or file_path.startswith(("❌", "⚠️")):
2141
+ return "⚠️ Please select a valid Python file.", gr.update(visible=False), "", "", ""
2142
+ if not instruction.strip():
2143
+ return "⚠️ Please provide refactoring instructions.", gr.update(visible=False), "", "", ""
2144
+
2145
+ if test_path == "None (Skip tests)":
2146
+ test_path = None
2147
+
2148
+ temp_dir = None
2149
+ try:
2150
+ temp_dir = tempfile.mkdtemp()
2151
+
2152
+ progress(0.2, desc="πŸ“¦ Extracting ZIP...")
2153
+ with zipfile.ZipFile(zip_path, 'r') as z:
2154
+ z.extractall(temp_dir)
2155
+
2156
+ progress(0.4, desc="πŸ“„ Reading files...")
2157
+
2158
+ # Find files
2159
+ target_file = None
2160
+ test_file = None
2161
+
2162
+ for root, dirs, files in os.walk(temp_dir):
2163
+ for f in files:
2164
+ if not f.endswith('.py'):
2165
+ continue
2166
+
2167
+ full_path = Path(root) / f
2168
+ rel_path = full_path.relative_to(temp_dir)
2169
+
2170
+ if str(rel_path).replace('\\', '/') == file_path.replace('\\', '/'):
2171
+ target_file = full_path
2172
+
2173
+ if test_path and str(rel_path).replace('\\', '/') == test_path.replace('\\', '/'):
2174
+ test_file = full_path
2175
+
2176
+ if not target_file:
2177
+ all_py = []
2178
+ for root, dirs, files in os.walk(temp_dir):
2179
+ for f in files:
2180
+ if f.endswith('.py'):
2181
+ rel = Path(root).relative_to(temp_dir) / f
2182
+ all_py.append(str(rel))
2183
+
2184
+ return f"❌ File not found: {file_path}\n\nπŸ“‚ Available:\n" + "\n".join(all_py[:15]), gr.update(visible=False), "", "", ""
2185
+
2186
+ # Read code
2187
+ original_code = target_file.read_text(encoding='utf-8')
2188
+ test_code = test_file.read_text(encoding='utf-8') if test_file else None
2189
+
2190
+ logger.info(f"βœ“ Target: {target_file.name} ({len(original_code)} chars)")
2191
+
2192
+ progress(0.6, desc="☁️ Sending to Modal (check Modal console for live progress)...")
2193
+
2194
+ # Call Modal
2195
+ try:
2196
+ import modal
2197
+ modal_fn = modal.Function.from_name("architect-ai-surgeon", "safe_refactor_and_test")
2198
+
2199
+ result = modal_fn.remote(original_code, instruction, test_code)
2200
+
2201
+ if not result["success"]:
2202
+ progress(1.0, desc="❌ Failed")
2203
+ return f"❌ Refactoring failed:\n{result['error']}", gr.update(visible=False), "", "", ""
2204
+
2205
+ if not result["test_results"]["passed"]:
2206
+ progress(1.0, desc="⚠️ Tests failed")
2207
+ return f"""⚠️ Code refactored but tests FAILED:
2208
+
2209
+ {result['test_results']['output']}
2210
+
2211
+ Code NOT saved. Fix tests first.
2212
+ """, gr.update(visible=False), "", "", ""
2213
+
2214
+ # Success - prepare download
2215
+ new_code = result["new_code"]
2216
+
2217
+ # Save to temp file for download
2218
+ download_path = Path(tempfile.gettempdir()) / f"refactored_{target_file.name}"
2219
+ download_path.write_text(new_code, encoding='utf-8')
2220
+
2221
+ # Preview (first 50 lines)
2222
+ preview_lines = new_code.split('\n')[:50]
2223
+ preview = '\n'.join(preview_lines)
2224
+ if len(new_code.split('\n')) > 50:
2225
+ preview += f"\n\n... ({len(new_code.split('\n')) - 50} more lines)"
2226
+
2227
+ progress(1.0, desc="βœ… Complete!")
2228
+
2229
+ output_msg = f"""βœ… Refactoring completed successfully!
2230
+
2231
+ πŸ“Š **Results:**
2232
+ - File: `{file_path}`
2233
+ - Original: {len(original_code)} chars, {len(original_code.split(chr(10)))} lines
2234
+ - Refactored: {len(new_code)} chars, {len(new_code.split(chr(10)))} lines
2235
+ - Change: {len(new_code) - len(original_code):+d} chars
2236
+
2237
+ πŸ§ͺ **Tests:** PASSED βœ“
2238
+
2239
+ πŸ“₯ **Download ready below** (see preview and download button)
2240
+
2241
+ **Test output:**
2242
+ ```
2243
+ {result['test_results']['output'][:500]}
2244
+ ```
2245
+
2246
+ πŸ’‘ **Check Modal console for detailed execution logs**
2247
+ """
2248
+
2249
+ return (
2250
+ output_msg,
2251
+ gr.update(visible=True), # Show download section
2252
+ preview, # Preview
2253
+ str(download_path), # Download file
2254
+ new_code # Store in state
2255
+ )
2256
+
2257
+ except ImportError:
2258
+ return "⚠️ Modal not installed. Run: pip install modal", gr.update(visible=False), "", "", ""
2259
+ except Exception as e:
2260
+ logger.error(f"Modal error: {e}")
2261
+ import traceback
2262
+ return f"❌ Modal failed: {e}\n\n{traceback.format_exc()[:500]}", gr.update(visible=False), "", "", ""
2263
+
2264
+ except Exception as e:
2265
+ logger.error(f"Error: {e}")
2266
+ import traceback
2267
+ return f"❌ Error: {e}\n\n{traceback.format_exc()[:500]}", gr.update(visible=False), "", "", ""
2268
+ finally:
2269
+ safe_cleanup(temp_dir)
2270
 
2271
+ # Auto-populate dropdowns
2272
  modal_zip.change(
2273
  fn=extract_file_list,
2274
  inputs=modal_zip,
2275
  outputs=[file_dropdown, test_dropdown]
2276
  )
2277
 
2278
+ # Execute button
2279
  execute_btn.click(
2280
+ fn=run_modal_refactoring_with_download,
2281
  inputs=[modal_zip, file_dropdown, instruction_input, test_dropdown],
2282
+ outputs=[modal_output, download_section, refactored_code_preview, download_file, refactored_code_state],
2283
+ show_progress="full"
2284
  )
2285
 
2286
+ # Prompt buttons
2287
+ prompt_strategy.click(lambda: PROMPTS["strategy"], outputs=instruction_input)
2288
+ prompt_factory.click(lambda: PROMPTS["factory"], outputs=instruction_input)
2289
+ prompt_singleton.click(lambda: PROMPTS["singleton"], outputs=instruction_input)
2290
+ prompt_observer.click(lambda: PROMPTS["observer"], outputs=instruction_input)
2291
+ prompt_docstrings.click(lambda: PROMPTS["docstrings"], outputs=instruction_input)
2292
+ prompt_typing.click(lambda: PROMPTS["typing"], outputs=instruction_input)
2293
+ prompt_error.click(lambda: PROMPTS["error"], outputs=instruction_input)
2294
+ prompt_logging.click(lambda: PROMPTS["logging"], outputs=instruction_input)
2295
+ prompt_async.click(lambda: PROMPTS["async"], outputs=instruction_input)
2296
+ prompt_cache.click(lambda: PROMPTS["cache"], outputs=instruction_input)
2297
+ prompt_optimize.click(lambda: PROMPTS["optimize"], outputs=instruction_input)
2298
+ prompt_lazy.click(lambda: PROMPTS["lazy"], outputs=instruction_input)
2299
 
2300
+
2301
+
2302
  # FOOTER
2303
  gr.HTML("""
2304
  <div class="footer">
core/__pycache__/llm_providers.cpython-313.pyc CHANGED
Binary files a/core/__pycache__/llm_providers.cpython-313.pyc and b/core/__pycache__/llm_providers.cpython-313.pyc differ
 
core/llm_providers.py CHANGED
@@ -177,8 +177,8 @@ class OpenAIProvider(LLMProvider):
177
  api_key=self.api_key,
178
  model=model or self.default_model,
179
  temperature=temperature,
180
- request_timeout=60.0, # 60 second timeout
181
- max_retries=3, # Retry up to 3 times on rate limits
182
  )
183
 
184
 
@@ -224,6 +224,6 @@ class GeminiProvider(LLMProvider):
224
  google_api_key=self.api_key,
225
  model=model or self.default_model,
226
  temperature=temperature,
227
- request_timeout=60.0,
228
  max_retries=3,
229
  )
 
177
  api_key=self.api_key,
178
  model=model or self.default_model,
179
  temperature=temperature,
180
+ request_timeout=10.0,
181
+ max_retries=3,
182
  )
183
 
184
 
 
224
  google_api_key=self.api_key,
225
  model=model or self.default_model,
226
  temperature=temperature,
227
+ request_timeout=10.0,
228
  max_retries=3,
229
  )
services/__pycache__/modal_executor.cpython-313.pyc CHANGED
Binary files a/services/__pycache__/modal_executor.cpython-313.pyc and b/services/__pycache__/modal_executor.cpython-313.pyc differ
 
services/modal_executor.py CHANGED
@@ -20,124 +20,179 @@ image = (
20
  app = modal.App("architect-ai-surgeon")
21
 
22
  # --- 2. Define the Remote Refactoring Function ---
23
- @app.function( # Changed decorator from @stub to @app
24
  image=image,
25
  secrets=[modal.Secret.from_name("my-openai-secret")],
26
  timeout=600,
27
  cpu=1.0,
28
  memory=1024
29
  )
30
- def safe_refactor_and_test(original_code: str, instruction: str, test_code: str = None) -> dict:
 
 
 
 
31
  """
32
- Run refactoring in the cloud.
 
33
  """
34
  from langchain_core.messages import SystemMessage, HumanMessage
35
  from langchain_openai import ChatOpenAI
 
 
 
 
 
 
 
 
 
 
36
 
37
- print(f"πŸ”§ Starting Cloud Refactoring task...")
38
-
39
- # --- PHASE 1: The Surgery (AI Refactoring) ---
 
40
  try:
41
- llm = ChatOpenAI(model="gpt-4o", temperature=0.0)
 
42
 
43
- system_prompt = (
44
- "You are a Senior Python Refactoring Engineer. "
45
- "Your goal is to rewrite the provided code to meet the user's architectural instructions "
46
- "while preserving the original business logic and passing all tests."
47
- )
48
 
49
- user_prompt = f"""
50
- **Task:** Refactor this Python code.
51
-
52
- **Instructions:** {instruction}
53
-
54
- **Constraints:**
55
- 1. Return ONLY the full valid Python code.
56
- 2. Do NOT use Markdown blocks (```python).
57
- 3. Do NOT add explanations or chat.
58
- 4. Preserve imports unless they need to change for the new architecture.
59
-
60
- **Original Code:**
61
- {original_code}
62
- """
63
 
 
 
 
 
64
  response = llm.invoke([
65
- SystemMessage(content=system_prompt),
66
- HumanMessage(content=user_prompt)
67
  ])
68
 
69
- new_code = response.content
70
- # Clean up markdown if present
71
- if new_code.startswith("```python"):
72
- new_code = new_code.replace("```python", "", 1)
73
- if new_code.startswith("```"):
74
- new_code = new_code.replace("```", "", 1)
75
- if new_code.endswith("```"):
76
- new_code = new_code[:-3]
77
 
78
- new_code = new_code.strip()
 
 
 
 
 
 
 
79
 
80
  except Exception as e:
 
 
81
  return {
82
  "success": False,
83
- "error": f"LLM Generation Failed: {str(e)}",
84
  "new_code": None,
85
- "test_results": {"passed": False, "output": "LLM Error"}
86
  }
87
 
88
- # --- PHASE 2: The Checkup (Verification & Testing) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  if not test_code:
90
- try:
91
- print("πŸ” No tests provided. Running Syntax Check...")
92
- compile(new_code, 'refactored_module.py', 'exec')
93
- test_results = {
 
 
 
 
 
94
  "passed": True,
95
- "output": "βœ… Syntax Check Passed. (No unit tests were provided for deep verification)."
96
- }
97
- except SyntaxError as e:
98
- return {
99
- "success": False,
100
- "error": f"Generated code has Syntax Errors: {e}",
101
- "new_code": new_code,
102
- "test_results": {"passed": False, "output": str(e)}
103
  }
104
-
105
- else:
106
- print("πŸ§ͺ Running provided Unit Tests...")
107
- with tempfile.TemporaryDirectory() as temp_dir:
108
- module_path = os.path.join(temp_dir, "target_module.py")
109
- with open(module_path, "w", encoding="utf-8") as f:
110
- f.write(new_code)
111
-
112
- test_path = os.path.join(temp_dir, "test_suite.py")
113
-
114
- # Adjust imports for the test environment
115
- # This handles imports like 'from services import...'
116
- adjusted_test_code = test_code.replace("from services import", "# from services import") \
117
- .replace("import target_module", "")
118
-
119
- with open(test_path, "w", encoding="utf-8") as f:
120
- f.write(test_code)
121
-
122
- env = os.environ.copy()
123
- env["PYTHONPATH"] = temp_dir
124
-
125
- result = subprocess.run(
126
- ["pytest", test_path],
127
- capture_output=True,
128
- text=True,
129
- env=env,
130
- cwd=temp_dir
131
- )
132
-
133
- test_results = {
134
- "passed": result.returncode == 0,
135
- "output": result.stdout + "\n" + result.stderr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  }
137
-
138
- return {
139
- "success": True,
140
- "error": None,
141
- "new_code": new_code,
142
- "test_results": test_results
143
- }
 
20
  app = modal.App("architect-ai-surgeon")
21
 
22
  # --- 2. Define the Remote Refactoring Function ---
23
+ @app.function(
24
  image=image,
25
  secrets=[modal.Secret.from_name("my-openai-secret")],
26
  timeout=600,
27
  cpu=1.0,
28
  memory=1024
29
  )
30
+ def safe_refactor_and_test(
31
+ original_code: str,
32
+ instruction: str,
33
+ test_code: str = None
34
+ ) -> dict:
35
  """
36
+ Refactor code in cloud with testing.
37
+ Shows progress in Modal console.
38
  """
39
  from langchain_core.messages import SystemMessage, HumanMessage
40
  from langchain_openai import ChatOpenAI
41
+ import ast
42
+
43
+ # ============ CONSOLE OUTPUT ============
44
+ print("=" * 60)
45
+ print("πŸš€ MODAL CLOUD REFACTORING SESSION")
46
+ print("=" * 60)
47
+ print(f"πŸ“‹ Instruction: {instruction[:100]}...")
48
+ print(f"πŸ“ Code size: {len(original_code)} characters")
49
+ print(f"πŸ§ͺ Tests: {'Provided' if test_code else 'None (syntax check only)'}")
50
+ print("-" * 60)
51
 
52
+ # ============ PHASE 1: AI REFACTORING ============
53
+ print("\nπŸ€– PHASE 1: AI REFACTORING")
54
+ print("-" * 60)
55
+
56
  try:
57
+ print("⏳ Initializing GPT-4o-mini...")
58
+ llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.0)
59
 
60
+ prompt = f"""Refactor this Python code according to these instructions:
 
 
 
 
61
 
62
+ **Instructions:** {instruction}
63
+
64
+ **Original Code:**
65
+ ```python
66
+ {original_code}
67
+ ```
 
 
 
 
 
 
 
 
68
 
69
+ Return ONLY the refactored Python code. No markdown blocks, no explanations.
70
+ """
71
+
72
+ print("⏳ Sending request to OpenAI...")
73
  response = llm.invoke([
74
+ SystemMessage(content="You are a Python refactoring expert. Return only valid Python code."),
75
+ HumanMessage(content=prompt)
76
  ])
77
 
78
+ new_code = response.content.strip()
 
 
 
 
 
 
 
79
 
80
+ # Clean markdown if present
81
+ if "```python" in new_code:
82
+ new_code = new_code.split("```python")[1].split("```")[0].strip()
83
+ elif "```" in new_code:
84
+ new_code = new_code.split("```")[1].split("```")[0].strip()
85
+
86
+ print(f"βœ… Refactored successfully ({len(new_code)} chars)")
87
+ print(f"πŸ“Š Code change: {len(new_code) - len(original_code):+d} characters")
88
 
89
  except Exception as e:
90
+ print(f"❌ LLM FAILED: {str(e)}")
91
+ print("=" * 60)
92
  return {
93
  "success": False,
94
+ "error": f"LLM failed: {str(e)}",
95
  "new_code": None,
96
+ "test_results": {"passed": False, "output": str(e)}
97
  }
98
 
99
+ # ============ PHASE 2: VALIDATION ============
100
+ print("\nπŸ” PHASE 2: VALIDATION")
101
+ print("-" * 60)
102
+
103
+ # Step 1: Syntax check
104
+ print("⏳ Checking syntax...")
105
+ try:
106
+ ast.parse(new_code)
107
+ print("βœ… Syntax valid")
108
+ except SyntaxError as e:
109
+ print(f"❌ SYNTAX ERROR at line {e.lineno}: {e.msg}")
110
+ print("=" * 60)
111
+ return {
112
+ "success": False,
113
+ "error": f"Syntax error: {e}",
114
+ "new_code": new_code,
115
+ "test_results": {"passed": False, "output": f"Line {e.lineno}: {e.msg}"}
116
+ }
117
+
118
+ # Step 2: Run tests (if provided)
119
  if not test_code:
120
+ print("ℹ️ No tests provided - skipping test execution")
121
+ print("=" * 60)
122
+ print("βœ… REFACTORING COMPLETE (Syntax OK)")
123
+ print("=" * 60)
124
+ return {
125
+ "success": True,
126
+ "error": None,
127
+ "new_code": new_code,
128
+ "test_results": {
129
  "passed": True,
130
+ "output": "βœ… Syntax check passed (no tests provided)"
 
 
 
 
 
 
 
131
  }
132
+ }
133
+
134
+ # Run pytest
135
+ print("\nπŸ§ͺ PHASE 3: TESTING")
136
+ print("-" * 60)
137
+ print(f"⏳ Running pytest on {len(test_code)} chars of test code...")
138
+
139
+ with tempfile.TemporaryDirectory() as tmp:
140
+ # Write refactored code
141
+ code_file = os.path.join(tmp, "refactored.py")
142
+ with open(code_file, "w") as f:
143
+ f.write(new_code)
144
+
145
+ # Write tests
146
+ test_file = os.path.join(tmp, "test_refactored.py")
147
+
148
+ # Fix imports in test
149
+ fixed_test = test_code.replace(
150
+ "from services.", "from refactored."
151
+ ).replace(
152
+ "import target_module", "import refactored as target_module"
153
+ )
154
+
155
+ with open(test_file, "w") as f:
156
+ f.write(fixed_test)
157
+
158
+ # Run pytest with verbose output
159
+ result = subprocess.run(
160
+ [sys.executable, "-m", "pytest", test_file, "-v", "--tb=short"],
161
+ capture_output=True,
162
+ text=True,
163
+ cwd=tmp,
164
+ env={**os.environ, "PYTHONPATH": tmp}
165
+ )
166
+
167
+ passed = result.returncode == 0
168
+
169
+ # Print test results to console
170
+ print("\nπŸ“Š TEST RESULTS:")
171
+ print("-" * 60)
172
+ if passed:
173
+ print("βœ… ALL TESTS PASSED")
174
+ else:
175
+ print("❌ TESTS FAILED")
176
+
177
+ print("\nTest Output:")
178
+ print(result.stdout)
179
+ if result.stderr:
180
+ print("\nErrors:")
181
+ print(result.stderr)
182
+
183
+ print("=" * 60)
184
+ if passed:
185
+ print("πŸŽ‰ REFACTORING SUCCESSFUL - ALL CHECKS PASSED")
186
+ else:
187
+ print("⚠️ REFACTORING COMPLETE BUT TESTS FAILED")
188
+ print("=" * 60)
189
+
190
+ return {
191
+ "success": True,
192
+ "error": None,
193
+ "new_code": new_code,
194
+ "test_results": {
195
+ "passed": passed,
196
+ "output": result.stdout + result.stderr
197
  }
198
+ }
 
 
 
 
 
 
services/pattern_detector.py CHANGED
@@ -805,7 +805,7 @@ class PatternDetectionService:
805
  def __init__(self, llm=None):
806
  self.llm = llm
807
  self.detector = None
808
- self.recommender = PatternRecommender()
809
  self.enricher = PatternEnricher(llm) if llm else None
810
 
811
  def analyze_code(self, code: str, enrich: bool = True) -> Dict[str, Any]:
 
805
  def __init__(self, llm=None):
806
  self.llm = llm
807
  self.detector = None
808
+ self.recommender = PatternRecommender(llm)
809
  self.enricher = PatternEnricher(llm) if llm else None
810
 
811
  def analyze_code(self, code: str, enrich: bool = True) -> Dict[str, Any]: