Phoenix21 commited on
Commit
0a5935a
Β·
verified Β·
1 Parent(s): 56a4aa7

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +783 -0
app.py ADDED
@@ -0,0 +1,783 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Code Flow Analyzer with Gradio Interface - Colab Compatible
4
+ A single-file application that uses LangChain agents to analyze code structure
5
+ and generate Mermaid.js flowchart diagrams through a web interface.
6
+
7
+ For Google Colab:
8
+ 1. Run this cell to install dependencies and start the app
9
+ 2. Enter your GROQ_API_KEY when prompted
10
+ 3. The Gradio interface will appear inline
11
+
12
+ For local usage:
13
+ 1. Set your API key: export GROQ_API_KEY="your-key-here"
14
+ 2. Run: python code_flow_analyzer.py
15
+ 3. Open the provided URL in your browser
16
+ """
17
+
18
+ import ast
19
+ import re
20
+ import os
21
+ import traceback
22
+ import sys
23
+ from typing import Dict, Any, List, Tuple
24
+ import getpass
25
+
26
+ # Check if running in Colab
27
+ try:
28
+ import google.colab
29
+ IN_COLAB = True
30
+ print("🟒 Running in Google Colab")
31
+ except ImportError:
32
+ IN_COLAB = False
33
+ print("🟑 Running locally")
34
+
35
+ # Install dependencies if in Colab
36
+ if IN_COLAB:
37
+ print("πŸ“¦ Installing dependencies...")
38
+ os.system("pip install -q gradio langchain langgraph langchain-groq")
39
+ print("βœ… Dependencies installed")
40
+
41
+ import gradio as gr
42
+ from langchain.chat_models import init_chat_model
43
+ from langchain.tools import tool
44
+ from langgraph.prebuilt import create_react_agent
45
+ from langgraph.checkpoint.memory import MemorySaver
46
+
47
+ # Sample code examples
48
+ SAMPLE_PYTHON = '''def main():
49
+ user_input = get_user_input()
50
+ if user_input:
51
+ result = process_data(user_input)
52
+ if result > 0:
53
+ display_result(result)
54
+ else:
55
+ show_error()
56
+ else:
57
+ show_help()
58
+
59
+ def get_user_input():
60
+ return input("Enter data: ")
61
+
62
+ def process_data(data):
63
+ for i in range(len(data)):
64
+ if data[i].isdigit():
65
+ return int(data[i])
66
+ return -1
67
+
68
+ def display_result(result):
69
+ print(f"Result: {result}")
70
+
71
+ def show_error():
72
+ print("Error processing data")
73
+
74
+ def show_help():
75
+ print("Please provide valid input")'''
76
+
77
+ SAMPLE_JAVASCRIPT = '''function calculateTotal(items) {
78
+ let total = 0;
79
+ for (let item of items) {
80
+ if (item.price > 0) {
81
+ total += item.price;
82
+ }
83
+ }
84
+ return total;
85
+ }
86
+
87
+ function processOrder(order) {
88
+ if (validateOrder(order)) {
89
+ const total = calculateTotal(order.items);
90
+ if (total > 100) {
91
+ applyDiscount(order);
92
+ }
93
+ return generateReceipt(order);
94
+ } else {
95
+ throw new Error("Invalid order");
96
+ }
97
+ }
98
+
99
+ function validateOrder(order) {
100
+ return order && order.items && order.items.length > 0;
101
+ }
102
+
103
+ function applyDiscount(order) {
104
+ order.discount = 0.1; // 10% discount
105
+ }
106
+
107
+ function generateReceipt(order) {
108
+ return {
109
+ items: order.items,
110
+ total: calculateTotal(order.items),
111
+ timestamp: new Date()
112
+ };
113
+ }'''
114
+
115
+ SAMPLE_JAVA = '''public class Calculator {
116
+ public static void main(String[] args) {
117
+ Calculator calc = new Calculator();
118
+ int result = calc.performCalculation();
119
+ calc.displayResult(result);
120
+ }
121
+
122
+ public int performCalculation() {
123
+ int a = getUserInput();
124
+ int b = getUserInput();
125
+
126
+ if (a > b) {
127
+ return multiply(a, b);
128
+ } else {
129
+ return add(a, b);
130
+ }
131
+ }
132
+
133
+ private int add(int x, int y) {
134
+ return x + y;
135
+ }
136
+
137
+ private int multiply(int x, int y) {
138
+ return x * y;
139
+ }
140
+
141
+ private int getUserInput() {
142
+ return 5; // Simplified for demo
143
+ }
144
+
145
+ private void displayResult(int result) {
146
+ System.out.println("Result: " + result);
147
+ }
148
+ }'''
149
+
150
+ # Handle API key setup
151
+ def setup_api_key():
152
+ """Setup API key for both Colab and local environments"""
153
+ api_key = os.getenv("GROQ_API_KEY")
154
+
155
+ if not api_key:
156
+ if IN_COLAB:
157
+ print("πŸ”‘ Please enter your Groq API key:")
158
+ print(" Get a free key from: https://console.groq.com/")
159
+ api_key = getpass.getpass("GROQ_API_KEY: ")
160
+ if api_key:
161
+ os.environ["GROQ_API_KEY"] = api_key
162
+ print("βœ… API key set successfully")
163
+ else:
164
+ print("⚠️ No API key provided - agent features will be disabled")
165
+ else:
166
+ print("⚠️ GROQ_API_KEY not found in environment variables")
167
+ print(" Get a free API key from: https://console.groq.com/")
168
+ print(" Set it with: export GROQ_API_KEY='your-key-here'")
169
+ else:
170
+ print("βœ… Groq API key found")
171
+
172
+ return api_key or os.getenv("GROQ_API_KEY")
173
+
174
+ # Setup API key
175
+ api_key = setup_api_key()
176
+
177
+ # Initialize LangChain components
178
+ model = None
179
+ memory = None
180
+ agent_executor = None
181
+
182
+ if api_key:
183
+ try:
184
+ model = init_chat_model("groq:qwen2.5-32b-instruct")
185
+ memory = MemorySaver()
186
+ print("βœ… Groq model initialized successfully")
187
+ except Exception as e:
188
+ print(f"❌ Could not initialize LangChain model: {e}")
189
+ print(" Please check your API key and internet connection")
190
+ model = None
191
+ memory = None
192
+
193
+ @tool
194
+ def analyze_code_structure(source_code: str) -> Dict[str, Any]:
195
+ """
196
+ Analyzes source code structure to identify functions, control flow, and dependencies.
197
+ Returns structured data about the code that can be used to generate flow diagrams.
198
+ """
199
+ try:
200
+ # Try to parse as Python first
201
+ try:
202
+ tree = ast.parse(source_code)
203
+ return _analyze_python_ast(tree)
204
+ except SyntaxError:
205
+ # If Python parsing fails, do basic text analysis
206
+ return _analyze_code_text(source_code)
207
+
208
+ except Exception as e:
209
+ return {"error": f"Analysis error: {str(e)}"}
210
+
211
+ def _analyze_python_ast(tree) -> Dict[str, Any]:
212
+ """Analyze Python AST"""
213
+ analysis = {
214
+ "functions": [],
215
+ "classes": [],
216
+ "control_flows": [],
217
+ "imports": [],
218
+ "call_graph": {}
219
+ }
220
+
221
+ class CodeAnalyzer(ast.NodeVisitor):
222
+ def __init__(self):
223
+ self.current_function = None
224
+
225
+ def visit_FunctionDef(self, node):
226
+ func_info = {
227
+ "name": node.name,
228
+ "line": node.lineno,
229
+ "args": [arg.arg for arg in node.args.args],
230
+ "calls": [],
231
+ "conditions": [],
232
+ "loops": []
233
+ }
234
+
235
+ self.current_function = node.name
236
+ analysis["call_graph"][node.name] = []
237
+
238
+ # Analyze function body
239
+ for child in ast.walk(node):
240
+ if isinstance(child, ast.Call):
241
+ if hasattr(child.func, 'id'):
242
+ func_info["calls"].append(child.func.id)
243
+ analysis["call_graph"][node.name].append(child.func.id)
244
+ elif hasattr(child.func, 'attr'):
245
+ func_info["calls"].append(child.func.attr)
246
+ elif isinstance(child, ast.If):
247
+ func_info["conditions"].append(f"if condition at line {child.lineno}")
248
+ elif isinstance(child, (ast.For, ast.While)):
249
+ loop_type = "for" if isinstance(child, ast.For) else "while"
250
+ func_info["loops"].append(f"{loop_type} loop at line {child.lineno}")
251
+
252
+ analysis["functions"].append(func_info)
253
+ self.generic_visit(node)
254
+
255
+ def visit_ClassDef(self, node):
256
+ class_info = {
257
+ "name": node.name,
258
+ "line": node.lineno,
259
+ "methods": []
260
+ }
261
+
262
+ for item in node.body:
263
+ if isinstance(item, ast.FunctionDef):
264
+ class_info["methods"].append(item.name)
265
+
266
+ analysis["classes"].append(class_info)
267
+ self.generic_visit(node)
268
+
269
+ def visit_Import(self, node):
270
+ for alias in node.names:
271
+ analysis["imports"].append(alias.name)
272
+ self.generic_visit(node)
273
+
274
+ def visit_ImportFrom(self, node):
275
+ module = node.module or ""
276
+ for alias in node.names:
277
+ analysis["imports"].append(f"{module}.{alias.name}")
278
+ self.generic_visit(node)
279
+
280
+ analyzer = CodeAnalyzer()
281
+ analyzer.visit(tree)
282
+ return analysis
283
+
284
+ def _analyze_code_text(source_code: str) -> Dict[str, Any]:
285
+ """Basic text-based code analysis for non-Python code"""
286
+ lines = source_code.split('\n')
287
+ analysis = {
288
+ "functions": [],
289
+ "classes": [],
290
+ "control_flows": [],
291
+ "imports": [],
292
+ "call_graph": {}
293
+ }
294
+
295
+ for i, line in enumerate(lines, 1):
296
+ line = line.strip()
297
+
298
+ # JavaScript function detection
299
+ js_func_match = re.match(r'function\s+(\w+)\s*\(', line)
300
+ if js_func_match:
301
+ func_name = js_func_match.group(1)
302
+ analysis["functions"].append({
303
+ "name": func_name,
304
+ "line": i,
305
+ "args": [],
306
+ "calls": [],
307
+ "conditions": [],
308
+ "loops": []
309
+ })
310
+ analysis["call_graph"][func_name] = []
311
+
312
+ # Java/C++ method detection
313
+ java_method_match = re.match(r'(?:public|private|protected)?\s*(?:static)?\s*\w+\s+(\w+)\s*\(', line)
314
+ if java_method_match and not js_func_match:
315
+ func_name = java_method_match.group(1)
316
+ if func_name not in ['class', 'if', 'for', 'while']: # Avoid keywords
317
+ analysis["functions"].append({
318
+ "name": func_name,
319
+ "line": i,
320
+ "args": [],
321
+ "calls": [],
322
+ "conditions": [],
323
+ "loops": []
324
+ })
325
+ analysis["call_graph"][func_name] = []
326
+
327
+ # Control structures
328
+ if re.match(r'\s*(if|else|elif|switch)\s*[\(\{]', line):
329
+ analysis["control_flows"].append(f"condition at line {i}")
330
+
331
+ if re.match(r'\s*(for|while|do)\s*[\(\{]', line):
332
+ analysis["control_flows"].append(f"loop at line {i}")
333
+
334
+ return analysis
335
+
336
+ @tool
337
+ def generate_mermaid_diagram(analysis_data: Dict[str, Any]) -> str:
338
+ """
339
+ Generates a Mermaid.js flowchart diagram from code analysis data.
340
+ Creates a visual representation of the code flow including function calls and control structures.
341
+ """
342
+ if "error" in analysis_data:
343
+ return f"flowchart TD\n Error[❌ {analysis_data['error']}]"
344
+
345
+ functions = analysis_data.get("functions", [])
346
+ call_graph = analysis_data.get("call_graph", {})
347
+
348
+ if not functions:
349
+ return """flowchart TD
350
+ Start([πŸš€ Program Start]) --> NoFunc[No Functions Found]
351
+ NoFunc --> End([🏁 Program End])
352
+
353
+ classDef startEnd fill:#e1f5fe,stroke:#01579b,stroke-width:2px
354
+ classDef warning fill:#fff3e0,stroke:#e65100,stroke-width:2px
355
+
356
+ class Start,End startEnd
357
+ class NoFunc warning"""
358
+
359
+ mermaid_lines = ["flowchart TD"]
360
+ mermaid_lines.append(" Start([πŸš€ Program Start]) --> Main")
361
+
362
+ # Create nodes for each function
363
+ func_nodes = []
364
+ for i, func in enumerate(functions):
365
+ func_name = func["name"]
366
+ safe_name = re.sub(r'[^a-zA-Z0-9_]', '_', func_name)
367
+ node_id = f"F{i}_{safe_name}"
368
+ func_nodes.append(node_id)
369
+
370
+ # Function node with emoji
371
+ mermaid_lines.append(f" {node_id}[βš™οΈ {func_name}()]")
372
+
373
+ # Add control structures within function
374
+ conditions = func.get("conditions", [])
375
+ loops = func.get("loops", [])
376
+
377
+ if conditions:
378
+ for j, condition in enumerate(conditions[:2]): # Limit to 2 conditions per function
379
+ cond_id = f"{node_id}_C{j}"
380
+ mermaid_lines.append(f" {node_id} --> {cond_id}{{πŸ€” Decision}}")
381
+ mermaid_lines.append(f" {cond_id} -->|Yes| {node_id}_Y{j}[βœ… True Path]")
382
+ mermaid_lines.append(f" {cond_id} -->|No| {node_id}_N{j}[❌ False Path]")
383
+
384
+ if loops:
385
+ for j, loop in enumerate(loops[:1]): # Limit to 1 loop per function
386
+ loop_id = f"{node_id}_L{j}"
387
+ loop_type = "πŸ”„ Loop" if "for" in loop else "⏰ While Loop"
388
+ mermaid_lines.append(f" {node_id} --> {loop_id}[{loop_type}]")
389
+ mermaid_lines.append(f" {loop_id} --> {loop_id}") # Self-loop
390
+
391
+ # Connect main flow
392
+ if func_nodes:
393
+ mermaid_lines.append(f" Main --> {func_nodes[0]}")
394
+
395
+ # Connect functions in sequence (simplified)
396
+ for i in range(len(func_nodes) - 1):
397
+ mermaid_lines.append(f" {func_nodes[i]} --> {func_nodes[i + 1]}")
398
+
399
+ # Connect to end
400
+ mermaid_lines.append(f" {func_nodes[-1]} --> End([🏁 Program End])")
401
+
402
+ # Add function call relationships (simplified to avoid clutter)
403
+ call_count = 0
404
+ for caller, callees in call_graph.items():
405
+ if call_count >= 3: # Limit number of call relationships
406
+ break
407
+ caller_node = None
408
+ for node in func_nodes:
409
+ if caller.lower() in node.lower():
410
+ caller_node = node
411
+ break
412
+
413
+ if caller_node:
414
+ for callee in callees[:2]: # Limit callees per function
415
+ callee_node = None
416
+ for node in func_nodes:
417
+ if callee.lower() in node.lower():
418
+ callee_node = node
419
+ break
420
+ if callee_node and callee_node != caller_node:
421
+ mermaid_lines.append(f" {caller_node} -.->|calls| {callee_node}")
422
+ call_count += 1
423
+
424
+ # Add styling
425
+ mermaid_lines.extend([
426
+ "",
427
+ " classDef startEnd fill:#e1f5fe,stroke:#01579b,stroke-width:3px,color:#000",
428
+ " classDef process fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,color:#000",
429
+ " classDef decision fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000",
430
+ " classDef success fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px,color:#000",
431
+ " classDef error fill:#ffebee,stroke:#c62828,stroke-width:2px,color:#000",
432
+ "",
433
+ " class Start,End startEnd",
434
+ f" class {','.join(func_nodes)} process" if func_nodes else ""
435
+ ])
436
+
437
+ return "\n".join(mermaid_lines)
438
+
439
+ @tool
440
+ def calculate_complexity_score(analysis_data: Dict[str, Any]) -> int:
441
+ """
442
+ Calculates a complexity score for the code based on various metrics.
443
+ Higher scores indicate more complex code structure.
444
+ """
445
+ if "error" in analysis_data:
446
+ return 0
447
+
448
+ score = 0
449
+ functions = analysis_data.get("functions", [])
450
+
451
+ # Base score for number of functions
452
+ score += len(functions) * 3
453
+
454
+ # Add score for control structures
455
+ for func in functions:
456
+ score += len(func.get("conditions", [])) * 4 # Conditions add complexity
457
+ score += len(func.get("loops", [])) * 3 # Loops add complexity
458
+ score += len(func.get("calls", [])) * 1 # Function calls add some complexity
459
+ score += len(func.get("args", [])) * 1 # Parameters add complexity
460
+
461
+ # Add score for classes
462
+ score += len(analysis_data.get("classes", [])) * 5
463
+
464
+ return min(score, 100) # Cap at 100
465
+
466
+ # Create the agent if model is available
467
+ if model and memory:
468
+ tools = [analyze_code_structure, generate_mermaid_diagram, calculate_complexity_score]
469
+ agent_executor = create_react_agent(model, tools, checkpointer=memory)
470
+ print("βœ… LangChain agent created successfully")
471
+ else:
472
+ agent_executor = None
473
+ print("❌ LangChain agent not available")
474
+
475
+ def analyze_code_with_agent(source_code: str, language: str = "auto") -> Tuple[str, str, List[str], int, str]:
476
+ """
477
+ Main function that uses the LangChain agent to analyze code and generate diagrams.
478
+ Returns: (mermaid_diagram, analysis_summary, functions_found, complexity_score, error_message)
479
+ """
480
+ if not source_code.strip():
481
+ return "", "No code provided", [], 0, "Please enter some source code to analyze"
482
+
483
+ if not agent_executor:
484
+ return "", "Agent not available", [], 0, "❌ LangChain agent not initialized. Please check your GROQ_API_KEY"
485
+
486
+ try:
487
+ # Detect language if auto
488
+ if language == "auto":
489
+ if "def " in source_code or "import " in source_code:
490
+ language = "Python"
491
+ elif "function " in source_code or "const " in source_code or "let " in source_code:
492
+ language = "JavaScript"
493
+ elif ("public " in source_code and "class " in source_code) or "System.out" in source_code:
494
+ language = "Java"
495
+ elif "#include" in source_code or "std::" in source_code:
496
+ language = "C++"
497
+ else:
498
+ language = "Unknown"
499
+
500
+ config = {"configurable": {"thread_id": f"session_{hash(source_code) % 10000}"}}
501
+
502
+ prompt = f"""
503
+ You are a code analysis expert. Analyze the following {language} source code and create a visual flow diagram.
504
+
505
+ Code to analyze:
506
+ ```{language.lower()}
507
+ {source_code}
508
+ ```
509
+
510
+ Please follow these steps exactly:
511
+ 1. First use the analyze_code_structure tool to understand the code structure
512
+ 2. Then use the generate_mermaid_diagram tool to create a flowchart
513
+ 3. Use the calculate_complexity_score tool to assess code complexity
514
+ 4. Provide a brief summary of what the code does and its structure
515
+
516
+ Important guidelines:
517
+ - Focus on the main flow between functions and important control structures
518
+ - Keep the diagram clean and readable
519
+ - Identify all functions and their relationships
520
+ - Note any loops, conditionals, and function calls
521
+ - Provide insights about code organization and complexity
522
+
523
+ Please be thorough in your analysis and make sure to use all three tools in order.
524
+ """
525
+
526
+ # Execute the agent
527
+ result = agent_executor.invoke(
528
+ {"messages": [{"role": "user", "content": prompt}]},
529
+ config
530
+ )
531
+
532
+ if result and "messages" in result:
533
+ response = result["messages"][-1].content
534
+
535
+ # Extract Mermaid diagram
536
+ mermaid_match = re.search(r'```mermaid\n(.*?)\n```', response, re.DOTALL)
537
+ if not mermaid_match:
538
+ mermaid_match = re.search(r'flowchart TD.*?(?=\n\n|\Z)', response, re.DOTALL)
539
+
540
+ mermaid_diagram = mermaid_match.group(1) if mermaid_match else mermaid_match.group(0) if mermaid_match else ""
541
+
542
+ # If no mermaid found in response, try to generate directly
543
+ if not mermaid_diagram:
544
+ print("⚠️ No Mermaid diagram found in response, generating fallback...")
545
+ # Fallback: analyze directly
546
+ analysis_result = analyze_code_structure.invoke({"source_code": source_code})
547
+ mermaid_diagram = generate_mermaid_diagram.invoke({"analysis_data": analysis_result})
548
+
549
+ # Extract complexity score
550
+ complexity_match = re.search(r'complexity.*?(\d+)', response, re.IGNORECASE)
551
+ complexity_score = int(complexity_match.group(1)) if complexity_match else 1
552
+
553
+ # Extract functions
554
+ functions_found = []
555
+ func_matches = re.findall(r'Functions found:.*?([^\n]+)', response, re.IGNORECASE)
556
+ if func_matches:
557
+ functions_found = [f.strip() for f in func_matches[0].split(',')]
558
+ else:
559
+ # Fallback: extract from analysis
560
+ analysis_result = analyze_code_structure.invoke({"source_code": source_code})
561
+ functions_found = [f["name"] for f in analysis_result.get("functions", [])]
562
+
563
+ # Clean up the response for summary
564
+ summary = re.sub(r'```mermaid.*?```', '', response, flags=re.DOTALL)
565
+ summary = re.sub(r'flowchart TD.*?(?=\n\n|\Z)', '', summary, flags=re.DOTALL)
566
+ summary = summary.strip()
567
+
568
+ return mermaid_diagram, summary, functions_found, complexity_score, ""
569
+
570
+ except Exception as e:
571
+ error_msg = f"❌ Analysis failed: {str(e)}"
572
+ print(f"Error details: {traceback.format_exc()}") # For debugging
573
+ return "", "", [], 0, error_msg
574
+
575
+ def create_gradio_interface():
576
+ """Create and configure the Gradio interface"""
577
+
578
+ def analyze_code_gradio(code, language):
579
+ """Wrapper function for Gradio interface"""
580
+ if not code.strip():
581
+ return (
582
+ "Please enter some code to analyze",
583
+ "",
584
+ "No analysis performed",
585
+ "Functions: 0 | Complexity: 0/100",
586
+ ""
587
+ )
588
+
589
+ mermaid, summary, functions, complexity, error = analyze_code_with_agent(code, language)
590
+
591
+ if error:
592
+ return (
593
+ error,
594
+ "",
595
+ "Analysis failed",
596
+ "Functions: 0 | Complexity: 0/100",
597
+ ""
598
+ )
599
+
600
+ # Format the outputs
601
+ mermaid_display = f"```mermaid\n{mermaid}\n```" if mermaid else "No diagram generated"
602
+ functions_display = f"**Functions Found:** {', '.join(functions)}" if functions else "No functions detected"
603
+ stats_display = f"Functions: {len(functions)} | Complexity: {complexity}/100"
604
+
605
+ return (
606
+ "βœ… Analysis completed successfully!",
607
+ mermaid_display,
608
+ summary,
609
+ stats_display,
610
+ functions_display
611
+ )
612
+
613
+ # Define the interface
614
+ with gr.Blocks(
615
+ title="πŸ”„ Code Flow Analyzer",
616
+ theme=gr.themes.Soft(),
617
+ css="""
618
+ .gradio-container {
619
+ max-width: 1200px !important;
620
+ }
621
+ .code-input {
622
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important;
623
+ }
624
+ """
625
+ ) as interface:
626
+
627
+ gr.Markdown("""
628
+ # πŸ”„ Code Flow Analyzer
629
+
630
+ **LangChain Agent + Mermaid.js** β€’ Visualize Your Code Flow
631
+
632
+ This tool uses AI agents to analyze your source code and generate visual flowchart diagrams.
633
+ """)
634
+
635
+ # API Status
636
+ api_status = "🟒 Groq LangChain Agent Ready (Qwen 2.5-32B)" if agent_executor else "πŸ”΄ Agent Not Available (Check GROQ_API_KEY)"
637
+ gr.Markdown(f"**Status:** {api_status}")
638
+
639
+ with gr.Row():
640
+ with gr.Column(scale=1):
641
+ gr.Markdown("### πŸ“ Source Code Input")
642
+
643
+ language_dropdown = gr.Dropdown(
644
+ choices=["auto", "Python", "JavaScript", "Java", "C++", "Other"],
645
+ value="auto",
646
+ label="Programming Language",
647
+ info="Auto-detection usually works well"
648
+ )
649
+
650
+ code_input = gr.TextArea(
651
+ placeholder="Paste your source code here...",
652
+ lines=15,
653
+ label="Source Code",
654
+ elem_classes=["code-input"]
655
+ )
656
+
657
+ with gr.Row():
658
+ gr.Examples(
659
+ examples=[
660
+ [SAMPLE_PYTHON, "Python"],
661
+ [SAMPLE_JAVASCRIPT, "JavaScript"],
662
+ [SAMPLE_JAVA, "Java"]
663
+ ],
664
+ inputs=[code_input, language_dropdown],
665
+ label="Quick Examples"
666
+ )
667
+
668
+ analyze_btn = gr.Button(
669
+ "πŸš€ Analyze Code Flow",
670
+ variant="primary",
671
+ size="lg"
672
+ )
673
+
674
+ with gr.Column(scale=1):
675
+ gr.Markdown("### πŸ“Š Analysis Results")
676
+
677
+ status_output = gr.Textbox(
678
+ label="Status",
679
+ interactive=False
680
+ )
681
+
682
+ stats_output = gr.Textbox(
683
+ label="Statistics",
684
+ interactive=False
685
+ )
686
+
687
+ functions_output = gr.Markdown(
688
+ label="Functions Found"
689
+ )
690
+
691
+ with gr.Row():
692
+ with gr.Column():
693
+ gr.Markdown("### 🎨 Generated Mermaid Diagram")
694
+ mermaid_output = gr.Textbox(
695
+ label="Mermaid Code",
696
+ lines=15,
697
+ max_lines=20,
698
+ interactive=True,
699
+ show_copy_button=True
700
+ )
701
+
702
+ gr.Markdown("""
703
+ **πŸ’‘ How to visualize:**
704
+ 1. Copy the Mermaid code above
705
+ 2. Visit [mermaid.live](https://mermaid.live)
706
+ 3. Paste and see your code flow diagram!
707
+
708
+ **πŸ“± For Colab users:**
709
+ - The Mermaid code above shows your program's flow structure
710
+ - Copy it to mermaid.live for a beautiful visual diagram
711
+ - Try the examples above to see different code patterns
712
+ """)
713
+
714
+ with gr.Row():
715
+ with gr.Column():
716
+ gr.Markdown("### πŸ“‹ Analysis Summary")
717
+ summary_output = gr.Textbox(
718
+ label="AI Agent Analysis",
719
+ lines=8,
720
+ interactive=False
721
+ )
722
+
723
+ # Connect the analyze button
724
+ analyze_btn.click(
725
+ fn=analyze_code_gradio,
726
+ inputs=[code_input, language_dropdown],
727
+ outputs=[status_output, mermaid_output, summary_output, stats_output, functions_output]
728
+ )
729
+
730
+ # Footer
731
+ environment_info = "Google Colab" if IN_COLAB else "Local Environment"
732
+ gr.Markdown(f"""
733
+ ---
734
+ **πŸ› οΈ Running in:** {environment_info}
735
+
736
+ **πŸ“¦ Dependencies:** gradio, langchain, langgraph, langchain-groq
737
+
738
+ **πŸ”§ Powered by:** LangChain Agents, Groq API (Qwen 2.5-32B), Mermaid.js, Gradio
739
+
740
+ **πŸ†“ Get Groq API Key:** [console.groq.com](https://console.groq.com/) (Free tier available!)
741
+ """)
742
+
743
+ return interface
744
+
745
+ def main():
746
+ """Main function to run the application"""
747
+ print("πŸ”„ Code Flow Analyzer with Gradio")
748
+ print("=" * 50)
749
+ print(f"🌐 Environment: {'Google Colab' if IN_COLAB else 'Local'}")
750
+
751
+ if agent_executor:
752
+ print("βœ… LangChain agent ready")
753
+ else:
754
+ print("❌ LangChain agent not available")
755
+ if IN_COLAB:
756
+ print(" πŸ’‘ Restart this cell and enter your GROQ_API_KEY when prompted")
757
+
758
+ print("\nπŸš€ Starting Gradio interface...")
759
+
760
+ # Create and launch the interface
761
+ interface = create_gradio_interface()
762
+
763
+ # Launch configuration for Colab vs local
764
+ if IN_COLAB:
765
+ # For Colab, use public sharing and specific settings
766
+ interface.launch(
767
+ share=True, # Enable public sharing for Colab
768
+ debug=False,
769
+ height=600,
770
+ show_error=True
771
+ )
772
+ else:
773
+ # For local usage
774
+ interface.launch(
775
+ server_name="0.0.0.0",
776
+ server_port=7860,
777
+ share=False, # Disable sharing for local use
778
+ debug=False
779
+ )
780
+
781
+ # Auto-run if in Colab or when script is executed directly
782
+ if __name__ == "__main__" or IN_COLAB:
783
+ main()