CodeViz / app.py
TD-jayadeera's picture
Implement initial project structure and setup
26ee80c
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
)