Spaces:
Sleeping
Sleeping
Commit
Β·
3a50c3e
1
Parent(s):
ee7688b
new commit
Browse files- README.md +1 -1
- app.py +504 -263
- core/__pycache__/llm_providers.cpython-313.pyc +0 -0
- core/llm_providers.py +3 -3
- services/__pycache__/modal_executor.cpython-313.pyc +0 -0
- services/modal_executor.py +146 -91
- services/pattern_detector.py +1 -1
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 = "
|
| 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 = "
|
| 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 = "
|
| 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 = "
|
| 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 = "
|
| 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="
|
| 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 = "
|
| 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 = "
|
| 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 = "
|
| 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 = "
|
| 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 = "
|
| 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 |
-
|
| 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("β"
|
| 1068 |
return "β οΈ Please select a valid Python file.", gr.update(visible=False)
|
| 1069 |
-
if not instruction
|
| 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 |
-
|
| 1077 |
try:
|
| 1078 |
-
|
| 1079 |
|
| 1080 |
-
progress(0.2, desc="π¦ Extracting
|
| 1081 |
-
with zipfile.ZipFile(zip_path, 'r') as
|
| 1082 |
-
|
| 1083 |
|
| 1084 |
-
progress(0.4, desc="π
|
| 1085 |
|
| 1086 |
-
#
|
| 1087 |
target_file = None
|
| 1088 |
-
|
| 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 |
-
|
| 1097 |
-
|
| 1098 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1099 |
|
| 1100 |
-
if not target_file
|
| 1101 |
-
|
| 1102 |
-
|
| 1103 |
-
for root, dirs, files in os.walk(temp_extract):
|
| 1104 |
for f in files:
|
| 1105 |
if f.endswith('.py'):
|
| 1106 |
-
|
| 1107 |
-
|
| 1108 |
|
| 1109 |
-
return f"β File not found: {file_path}\n\
|
| 1110 |
|
| 1111 |
-
|
| 1112 |
original_code = target_file.read_text(encoding='utf-8')
|
|
|
|
| 1113 |
|
| 1114 |
-
|
| 1115 |
-
|
| 1116 |
-
|
| 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.
|
| 1128 |
|
| 1129 |
-
#
|
| 1130 |
try:
|
| 1131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1132 |
|
| 1133 |
-
#
|
| 1134 |
-
|
| 1135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1136 |
|
| 1137 |
progress(1.0, desc="β
Complete!")
|
| 1138 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1139 |
|
| 1140 |
except ImportError:
|
| 1141 |
-
return "β οΈ Modal not
|
| 1142 |
except Exception as modal_error:
|
| 1143 |
logger.error(f"Modal error: {modal_error}")
|
| 1144 |
-
|
|
|
|
| 1145 |
|
| 1146 |
except Exception as e:
|
| 1147 |
logger.error(f"Refactoring error: {e}")
|
| 1148 |
-
|
|
|
|
| 1149 |
finally:
|
| 1150 |
-
safe_cleanup(
|
| 1151 |
-
|
|
|
|
| 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="
|
| 1314 |
label="LLM Provider",
|
| 1315 |
scale=1
|
| 1316 |
)
|
|
@@ -1322,14 +1300,60 @@ with gr.Blocks(
|
|
| 1322 |
elem_classes=["primary-button"]
|
| 1323 |
)
|
| 1324 |
|
| 1325 |
-
|
| 1326 |
-
|
| 1327 |
-
|
| 1328 |
-
|
| 1329 |
-
|
| 1330 |
-
|
| 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 |
-
# ---
|
| 1345 |
-
with gr.Row(visible=
|
| 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=
|
| 1349 |
recommendation_dropdown_single = gr.Dropdown(
|
| 1350 |
label="π Select Recommendation to Visualize",
|
| 1351 |
choices=[],
|
| 1352 |
interactive=True
|
| 1353 |
)
|
| 1354 |
|
| 1355 |
-
with gr.Row(visible=
|
| 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=
|
| 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="
|
| 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=
|
| 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=
|
| 1542 |
recommendation_dropdown = gr.Dropdown(
|
| 1543 |
label="π Select Recommendation to Visualize",
|
| 1544 |
choices=[],
|
| 1545 |
interactive=True
|
| 1546 |
)
|
| 1547 |
|
| 1548 |
-
with gr.Row(visible=
|
| 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 |
-
|
| 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="
|
| 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 |
-
|
| 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="
|
| 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 |
-
|
| 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="
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2037 |
|
| 2038 |
-
# Auto-populate dropdowns
|
| 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=
|
| 2047 |
inputs=[modal_zip, file_dropdown, instruction_input, test_dropdown],
|
| 2048 |
-
outputs=[modal_output,
|
|
|
|
| 2049 |
)
|
| 2050 |
|
| 2051 |
-
#
|
| 2052 |
-
|
| 2053 |
-
|
| 2054 |
-
|
| 2055 |
-
|
| 2056 |
-
|
| 2057 |
-
|
| 2058 |
-
|
| 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=
|
| 181 |
-
max_retries=3,
|
| 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=
|
| 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(
|
| 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 |
"""
|
| 32 |
-
|
|
|
|
| 33 |
"""
|
| 34 |
from langchain_core.messages import SystemMessage, HumanMessage
|
| 35 |
from langchain_openai import ChatOpenAI
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
|
|
|
| 40 |
try:
|
| 41 |
-
|
|
|
|
| 42 |
|
| 43 |
-
|
| 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 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 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=
|
| 66 |
-
HumanMessage(content=
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
except Exception as e:
|
|
|
|
|
|
|
| 81 |
return {
|
| 82 |
"success": False,
|
| 83 |
-
"error": f"LLM
|
| 84 |
"new_code": None,
|
| 85 |
-
"test_results": {"passed": False, "output":
|
| 86 |
}
|
| 87 |
|
| 88 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
if not test_code:
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
"passed": True,
|
| 95 |
-
"output": "β
Syntax
|
| 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 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
}
|
| 137 |
-
|
| 138 |
-
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]:
|