Spaces:
Sleeping
Sleeping
File size: 9,049 Bytes
26ee80c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | 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
)
|