Spaces:
Sleeping
Sleeping
| import os | |
| import uuid | |
| import gradio as gr | |
| from openai import OpenAI | |
| # Get API key from environment | |
| api_key = os.environ.get("NEBIUS_API_KEY") | |
| if not api_key: | |
| print("Warning: NEBIUS_API_KEY environment variable not set. LLM features will be disabled.") | |
| client = None | |
| else: | |
| client = OpenAI( | |
| base_url="https://api.studio.nebius.com/v1/", | |
| api_key=api_key, | |
| ) | |
| # Import visualization tools and helper | |
| from tools.visualizer_linked_list import generate_linked_list_gif | |
| from tools.visualizer_sort import generate_bubble_sort_gif | |
| from tools.utility_helpers import ensure_output_dir | |
| from tools.code_analyzer import CodeAnalyzer | |
| ensure_output_dir() # make sure ./output/ exists | |
| code_analyzer = CodeAnalyzer() | |
| PLACEHOLDER_IMG = os.path.join("output", "no_visualization.gif") | |
| def create_placeholder_gif(): | |
| # Create a simple placeholder GIF if it doesn't exist | |
| if not os.path.exists(PLACEHOLDER_IMG): | |
| from PIL import Image, ImageDraw | |
| img = Image.new('RGB', (400, 100), color='white') | |
| d = ImageDraw.Draw(img) | |
| d.text((10, 40), "No visualization available", fill=(0, 0, 0)) | |
| img.save(PLACEHOLDER_IMG, format='GIF') | |
| create_placeholder_gif() | |
| def explain_algorithm_with_llm(code_snippet: str, code_info: dict = None): | |
| """ | |
| Send a prompt to Nebius-hosted LLM (Meta-Llama-3.1-70B-Instruct). | |
| Returns the model's explanation text. | |
| """ | |
| if not client: | |
| return "LLM features are disabled. Please set the NEBIUS_API_KEY environment variable to enable AI explanations." | |
| if code_info: | |
| prompt = code_analyzer.get_explanation_prompt(code_info) | |
| prompt += f"\n\nCode:\n{code_snippet}" | |
| else: | |
| prompt = f"Please explain this code step by step:\n\n{code_snippet}" | |
| response = client.chat.completions.create( | |
| model="meta-llama/Meta-Llama-3.1-70B-Instruct", | |
| messages=[{"role": "user", "content": prompt}], | |
| temperature=0.6 | |
| ) | |
| return response.choices[0].message.content | |
| def extract_array_from_code(code: str): | |
| import re | |
| # Try to find Python list or comma-separated numbers | |
| array_match = re.search(r'\[([\d\s,]+)\]', code) | |
| if array_match: | |
| try: | |
| return [int(x.strip()) for x in array_match.group(1).split(',') if x.strip()] | |
| except Exception: | |
| pass | |
| # Try to find comma-separated numbers in the code | |
| csv_match = re.search(r'(\d+(?:\s*,\s*\d+)+)', code) | |
| if csv_match: | |
| try: | |
| return [int(x.strip()) for x in csv_match.group(1).split(',') if x.strip()] | |
| except Exception: | |
| pass | |
| return None | |
| def extract_linked_list_ops_from_code(code: str): | |
| import re | |
| operations = [] | |
| for line in code.split('\n'): | |
| if 'insert' in line.lower(): | |
| num_match = re.search(r'insert\s+(\d+)', line.lower()) | |
| if num_match: | |
| operations.append(f"insert {num_match.group(1)}") | |
| elif 'delete' in line.lower(): | |
| operations.append("delete") | |
| return operations if operations else None | |
| def generate_visualization(code_info: dict, code: str) -> str: | |
| """ | |
| Generate appropriate visualization based on code analysis. | |
| Returns the path to the generated GIF. | |
| """ | |
| gif_name = f"{uuid.uuid4()}.gif" | |
| output_path = os.path.join("output", gif_name) | |
| # Extract values for visualization from the code | |
| try: | |
| # For sorting algorithms, try to find array/list in the code | |
| if any('sort' in algo for algo in code_info['patterns']['algorithms']): | |
| values = extract_array_from_code(code) | |
| if values: | |
| generate_bubble_sort_gif(values, output_path) | |
| return output_path | |
| # For linked list operations | |
| if any('linked_list' in ds for ds in code_info['patterns']['data_structures']): | |
| operations = extract_linked_list_ops_from_code(code) | |
| if operations: | |
| generate_linked_list_gif(operations, output_path) | |
| return output_path | |
| except Exception as e: | |
| print(f"Error generating visualization: {e}") | |
| return PLACEHOLDER_IMG | |
| def process_code(code_snippet: str): | |
| """ | |
| Process the input code: | |
| 1. Analyze the code | |
| 2. Generate visualization | |
| 3. Get LLM explanation | |
| """ | |
| # Analyze the code | |
| code_info = code_analyzer.analyze_code(code_snippet) | |
| debug_log = [] | |
| if code_info.get('type') == 'error': | |
| return f"โ Code analysis failed: {code_info.get('error')}", PLACEHOLDER_IMG | |
| # Generate visualization | |
| visualization_path = generate_visualization(code_info, code_snippet) | |
| no_visual = (visualization_path == PLACEHOLDER_IMG) | |
| # Get LLM explanation | |
| explanation = explain_algorithm_with_llm(code_snippet, code_info) | |
| # Create a beautiful markdown output | |
| markdown_output = f""" | |
| # ๐ Code Analysis Report | |
| ## ๐ฏ Code Type & Name | |
| - **Type:** {code_info['type'].title()} | |
| {f"- **Name:** `{code_info['name']}`" if code_info['name'] else ""} | |
| - **Complexity:** `{code_info['complexity']}` | |
| ## ๐ Detected Patterns | |
| """ | |
| if code_info['patterns']['algorithms']: | |
| markdown_output += "\n### ๐งฎ Algorithms\n" | |
| for algo in code_info['patterns']['algorithms']: | |
| markdown_output += f"- {algo.replace(':', ': ').title()}\n" | |
| if code_info['patterns']['data_structures']: | |
| markdown_output += "\n### ๐ Data Structures\n" | |
| for ds in code_info['patterns']['data_structures']: | |
| markdown_output += f"- {ds.replace(':', ': ').title()}\n" | |
| markdown_output += "\n## ๐ Code Structure\n" | |
| structure = code_info['structure'] | |
| markdown_output += f"- {'โ ' if structure['has_recursion'] else 'โ'} Recursion\n" | |
| markdown_output += f"- {'โ ' if structure['has_classes'] else 'โ'} Classes\n" | |
| markdown_output += f"- {'โ ' if structure['has_functions'] else 'โ'} Functions\n" | |
| markdown_output += f"- {'โ ' if structure['has_loops'] else 'โ'} Loops\n" | |
| if no_visual: | |
| markdown_output += """ | |
| ## โ ๏ธ Visualization Note | |
| No visualization could be generated for this code. | |
| **Supported Visualizations:** | |
| - Bubble Sort (with array) | |
| - Linked List (with 'insert N'/'delete' lines) | |
| """ | |
| markdown_output += "\n## ๐ Explanation\n" | |
| markdown_output += explanation | |
| return markdown_output, visualization_path | |
| # Custom CSS | |
| css = """ | |
| .code-input { | |
| font-family: 'Consolas', 'Monaco', monospace !important; | |
| font-size: 14px !important; | |
| } | |
| .explanation-output { | |
| font-size: 14px !important; | |
| line-height: 1.6 !important; | |
| } | |
| .visualization-output { | |
| border-radius: 8px !important; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important; | |
| } | |
| """ | |
| # Gradio UI | |
| with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo: | |
| gr.Markdown(""" | |
| # ๐ง CodeViz: Visual Learning of Algorithms | |
| Enter your code below to get: | |
| 1. A detailed explanation of how it works | |
| 2. A visualization of the algorithm/data structure | |
| 3. Analysis of its complexity and patterns | |
| Examples: | |
| ```python | |
| # Bubble Sort | |
| def bubble_sort(arr): | |
| n = len(arr) | |
| for i in range(n): | |
| for j in range(0, n-i-1): | |
| if arr[j] > arr[j+1]: | |
| arr[j], arr[j+1] = arr[j+1], arr[j] | |
| return arr | |
| # Test with array | |
| arr = [64, 34, 25, 12, 22, 11, 90] | |
| ``` | |
| ```python | |
| # Linked List Insert | |
| class Node: | |
| def __init__(self, data): | |
| self.data = data | |
| self.next = None | |
| def insert_at_end(head, data): | |
| new_node = Node(data) | |
| if head is None: | |
| return new_node | |
| current = head | |
| while current.next: | |
| current = current.next | |
| current.next = new_node | |
| return head | |
| ``` | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| code_input = gr.Textbox( | |
| label="Enter your code", | |
| lines=10, | |
| placeholder="Paste your code here...", | |
| elem_classes=["code-input"], | |
| type="text" | |
| ) | |
| run_btn = gr.Button("Visualize & Explain", variant="primary") | |
| with gr.Column(scale=1): | |
| explanation_output = gr.Markdown( | |
| label="Explanation & Analysis", | |
| elem_classes=["explanation-output"] | |
| ) | |
| visualization_output = gr.Image( | |
| label="Visualization", | |
| type="filepath", | |
| elem_classes=["visualization-output"] | |
| ) | |
| run_btn.click( | |
| fn=process_code, | |
| inputs=[code_input], | |
| outputs=[explanation_output, visualization_output], | |
| api_name="process_code" | |
| ) | |
| if __name__ == "__main__": | |
| demo.queue().launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=True, # Enable sharing for easier access | |
| show_error=True # Show detailed error messages | |
| ) | |