From b0e6254a29fbf66fd8d6d1bc3677aa3104f4d64d Mon Sep 17 00:00:00 2001 From: Philip Omoigui Date: Sun, 2 Nov 2025 13:19:41 +0100 Subject: [PATCH 1/2] Added Week 4 exercise: AI-Powered Code Review Assistant --- .../philip/week4_EXERCISE.ipynb | 686 ++++++++++++++++++ 1 file changed, 686 insertions(+) create mode 100644 week4/community-contributions/philip/week4_EXERCISE.ipynb diff --git a/week4/community-contributions/philip/week4_EXERCISE.ipynb b/week4/community-contributions/philip/week4_EXERCISE.ipynb new file mode 100644 index 0000000..039e2e2 --- /dev/null +++ b/week4/community-contributions/philip/week4_EXERCISE.ipynb @@ -0,0 +1,686 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import json\n", + "import re\n", + "from typing import List, Dict, Optional\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "import gradio as gr\n", + "from IPython.display import Markdown, display\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Groq API Key not set (optional)\n", + "OpenRouter API Key loaded (begins with sk-or-)\n" + ] + } + ], + "source": [ + "load_dotenv(override=True)\n", + "\n", + "# Ollama connection \n", + "ollama_url = \"http://localhost:11434/v1\"\n", + "ollama_client = OpenAI(api_key=\"ollama\", base_url=ollama_url)\n", + "\n", + "# Groq connection\n", + "groq_api_key = os.getenv('GROQ_API_KEY')\n", + "groq_url = \"https://api.groq.com/openai/v1\"\n", + "groq_client = None\n", + "if groq_api_key:\n", + " groq_client = OpenAI(api_key=groq_api_key, base_url=groq_url)\n", + " print(f\"Groq API Key loaded (begins with {groq_api_key[:4]})\")\n", + "else:\n", + " print(\"Groq API Key not set (optional)\")\n", + "\n", + "# OpenRouter connection\n", + "openrouter_api_key = os.getenv('OPENROUTER_API_KEY')\n", + "openrouter_url = \"https://openrouter.ai/api/v1\"\n", + "openrouter_client = None\n", + "if openrouter_api_key:\n", + " openrouter_client = OpenAI(api_key=openrouter_api_key, base_url=openrouter_url)\n", + " print(f\"OpenRouter API Key loaded (begins with {openrouter_api_key[:6]})\")\n", + "else:\n", + " print(\"OpenRouter API Key not set (optional)\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configured 3 models\n", + "OpenRouter models available (perfect for limited storage demos!)\n" + ] + } + ], + "source": [ + "# Open-source code models configuration\n", + "MODELS = {}\n", + "\n", + "if groq_client:\n", + " MODELS.update({\n", + " \"gpt-oss-20b-groq\": {\n", + " \"name\": \"GPT-OSS-20B (Groq)\",\n", + " \"client\": groq_client,\n", + " \"model\": \"gpt-oss:20b\",\n", + " \"description\": \"Cloud\"\n", + " },\n", + " \"gpt-oss-120b-groq\": {\n", + " \"name\": \"GPT-OSS-120B (Groq)\",\n", + " \"client\": groq_client,\n", + " \"model\": \"openai/gpt-oss-120b\",\n", + " \"description\": \"Cloud - Larger GPT-OSS\"\n", + " },\n", + " \"qwen2.5-coder-32b-groq\": {\n", + " \"name\": \"Qwen2.5-Coder 32B (Groq)\",\n", + " \"client\": groq_client,\n", + " \"model\": \"qwen/qwen2.5-coder-32b-instruct\",\n", + " \"description\": \"Cloud\"\n", + " },\n", + " })\n", + "\n", + "# OpenRouter models\n", + "if openrouter_client:\n", + " MODELS.update({\n", + " \"qwen-2.5-coder-32b-openrouter\": {\n", + " \"name\": \"Qwen2.5-Coder 32B (OpenRouter)\",\n", + " \"client\": openrouter_client,\n", + " \"model\": \"qwen/qwen-2.5-coder-32b-instruct\",\n", + " \"description\": \"Cloud - Perfect for demos, 50 req/day free\"\n", + " },\n", + " \"gpt-oss-20b-groq\": {\n", + " \"name\": \"GPT-OSS-20B\",\n", + " \"client\": openrouter_client,\n", + " \"model\": \"openai/gpt-oss-20b\",\n", + " \"description\": \"Cloud - OpenAI's open model, excellent for code!\"\n", + " },\n", + " })\n", + "\n", + "print(f\"Configured {len(MODELS)} models\")\n", + "if openrouter_client:\n", + " print(\"OpenRouter models available (perfect for limited storage demos!)\")\n", + "if groq_client:\n", + " print(\"Groq models available (fast cloud inference!)\")\n", + "if \"qwen2.5-coder:7b\" in MODELS:\n", + " print(\"Ollama models available (unlimited local usage!)\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "BUG_DETECTION_SYSTEM_PROMPT = \"\"\"You are an expert code reviewer specializing in finding bugs, security vulnerabilities, and logic errors.\n", + "\n", + "Your task is to analyze Python code and identify issues. Return ONLY a valid JSON array with this exact format:\n", + "[{\n", + " \"severity\": \"critical|high|medium|low\",\n", + " \"line\": number,\n", + " \"issue\": \"brief description of the problem\",\n", + " \"suggestion\": \"specific fix recommendation\"\n", + "}]\n", + "\n", + "Be thorough but concise. Focus on real bugs and security issues.\"\"\"\n", + "\n", + "IMPROVEMENTS_SYSTEM_PROMPT = \"\"\"You are a senior software engineer specializing in code quality and best practices.\n", + "\n", + "Analyze the Python code and suggest improvements for:\n", + "- Code readability and maintainability\n", + "- Performance optimizations\n", + "- Pythonic idioms and conventions\n", + "- Better error handling\n", + "\n", + "Return ONLY a JSON array:\n", + "[{\n", + " \"category\": \"readability|performance|style|error_handling\",\n", + " \"line\": number,\n", + " \"current\": \"current code snippet\",\n", + " \"improved\": \"improved code snippet\",\n", + " \"explanation\": \"why this is better\"\n", + "}]\n", + "\n", + "Only suggest meaningful improvements.\"\"\"\n", + "\n", + "TEST_GENERATION_SYSTEM_PROMPT = \"\"\"You are an expert in writing comprehensive unit tests.\n", + "\n", + "Generate pytest unit tests for the given Python code. Include:\n", + "- Test cases for normal operation\n", + "- Edge cases and boundary conditions\n", + "- Error handling tests\n", + "- Tests for any bugs that were identified\n", + "\n", + "Return ONLY Python code with pytest tests. Include the original code at the top if needed.\n", + "Do not include explanations or markdown formatting.\"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def extract_json_from_response(text: str) -> List[Dict]:\n", + " \"\"\"Extract JSON array from model response, handling markdown code blocks.\"\"\"\n", + " # Remove markdown code blocks\n", + " text = re.sub(r'```json\\n?', '', text)\n", + " text = re.sub(r'```\\n?', '', text)\n", + " \n", + " # Try to find JSON array\n", + " json_match = re.search(r'\\[\\s*\\{.*\\}\\s*\\]', text, re.DOTALL)\n", + " if json_match:\n", + " try:\n", + " return json.loads(json_match.group())\n", + " except json.JSONDecodeError:\n", + " pass\n", + " \n", + " # Fallback: try parsing entire response\n", + " try:\n", + " return json.loads(text.strip())\n", + " except json.JSONDecodeError:\n", + " return []\n", + "\n", + "def detect_bugs(code: str, model_key: str) -> Dict:\n", + " \"\"\"Detect bugs and security issues in code.\"\"\"\n", + " model_config = MODELS[model_key]\n", + " client = model_config[\"client\"]\n", + " model_name = model_config[\"model\"]\n", + " \n", + " user_prompt = f\"Analyze this Python code for bugs and security issues:\\n\\n```python\\n{code}\\n```\"\n", + " \n", + " try:\n", + " response = client.chat.completions.create(\n", + " model=model_name,\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": BUG_DETECTION_SYSTEM_PROMPT},\n", + " {\"role\": \"user\", \"content\": user_prompt}\n", + " ],\n", + " temperature=0.1\n", + " )\n", + " \n", + " content = response.choices[0].message.content\n", + " issues = extract_json_from_response(content)\n", + " \n", + " return {\n", + " \"model\": model_config[\"name\"],\n", + " \"issues\": issues,\n", + " \"raw_response\": content,\n", + " \"success\": True\n", + " }\n", + " except Exception as e:\n", + " return {\n", + " \"model\": model_config[\"name\"],\n", + " \"issues\": [],\n", + " \"error\": str(e),\n", + " \"success\": False\n", + " }\n", + "\n", + "def suggest_improvements(code: str, model_key: str) -> Dict:\n", + " \"\"\"Suggest code improvements and best practices.\"\"\"\n", + " model_config = MODELS[model_key]\n", + " client = model_config[\"client\"]\n", + " model_name = model_config[\"model\"]\n", + " \n", + " user_prompt = f\"Suggest improvements for this Python code:\\n\\n```python\\n{code}\\n```\"\n", + " \n", + " try:\n", + " response = client.chat.completions.create(\n", + " model=model_name,\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": IMPROVEMENTS_SYSTEM_PROMPT},\n", + " {\"role\": \"user\", \"content\": user_prompt}\n", + " ],\n", + " temperature=0.2\n", + " )\n", + " \n", + " content = response.choices[0].message.content\n", + " improvements = extract_json_from_response(content)\n", + " \n", + " return {\n", + " \"model\": model_config[\"name\"],\n", + " \"improvements\": improvements,\n", + " \"raw_response\": content,\n", + " \"success\": True\n", + " }\n", + " except Exception as e:\n", + " return {\n", + " \"model\": model_config[\"name\"],\n", + " \"improvements\": [],\n", + " \"error\": str(e),\n", + " \"success\": False\n", + " }\n", + "\n", + "def generate_tests(code: str, bugs: List[Dict], model_key: str) -> Dict:\n", + " \"\"\"Generate unit tests for the code.\"\"\"\n", + " model_config = MODELS[model_key]\n", + " client = model_config[\"client\"]\n", + " model_name = model_config[\"model\"]\n", + " \n", + " bugs_context = \"\"\n", + " if bugs:\n", + " bugs_context = f\"\\n\\nNote: The following bugs were identified:\\n\" + \"\\n\".join([f\"- Line {b.get('line', '?')}: {b.get('issue', '')}\" for b in bugs])\n", + " \n", + " user_prompt = f\"Generate pytest unit tests for this Python code:{bugs_context}\\n\\n```python\\n{code}\\n```\"\n", + " \n", + " try:\n", + " response = client.chat.completions.create(\n", + " model=model_name,\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": TEST_GENERATION_SYSTEM_PROMPT},\n", + " {\"role\": \"user\", \"content\": user_prompt}\n", + " ],\n", + " temperature=0.3\n", + " )\n", + " \n", + " content = response.choices[0].message.content\n", + " # Remove markdown code blocks if present\n", + " test_code = re.sub(r'```python\\n?', '', content)\n", + " test_code = re.sub(r'```\\n?', '', test_code)\n", + " \n", + " return {\n", + " \"model\": model_config[\"name\"],\n", + " \"test_code\": test_code.strip(),\n", + " \"raw_response\": content,\n", + " \"success\": True\n", + " }\n", + " except Exception as e:\n", + " return {\n", + " \"model\": model_config[\"name\"],\n", + " \"test_code\": \"\",\n", + " \"error\": str(e),\n", + " \"success\": False\n", + " }\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def format_bugs_output(result: Dict) -> str:\n", + " \"\"\"Format bug detection results for display.\"\"\"\n", + " if not result.get(\"success\"):\n", + " return f\"**Error with {result['model']}:** {result.get('error', 'Unknown error')}\"\n", + " \n", + " issues = result.get(\"issues\", [])\n", + " if not issues:\n", + " return f\"โœ… **{result['model']}**: No issues found. Code looks good!\"\n", + " \n", + " output = [f\"**{result['model']}** - Found {len(issues)} issue(s):\\n\"]\n", + " \n", + " severity_order = {\"critical\": 0, \"high\": 1, \"medium\": 2, \"low\": 3}\n", + " sorted_issues = sorted(issues, key=lambda x: severity_order.get(x.get(\"severity\", \"low\"), 3))\n", + " \n", + " for issue in sorted_issues:\n", + " severity = issue.get(\"severity\", \"unknown\").upper()\n", + " line = issue.get(\"line\", \"?\")\n", + " issue_desc = issue.get(\"issue\", \"\")\n", + " suggestion = issue.get(\"suggestion\", \"\")\n", + " \n", + " severity_emoji = {\n", + " \"CRITICAL\": \"๐Ÿ”ด\",\n", + " \"HIGH\": \"๐ŸŸ \",\n", + " \"MEDIUM\": \"๐ŸŸก\",\n", + " \"LOW\": \"๐Ÿ”ต\"\n", + " }.get(severity, \"โšช\")\n", + " \n", + " output.append(f\"{severity_emoji} **{severity}** (Line {line}): {issue_desc}\")\n", + " if suggestion:\n", + " output.append(f\" ๐Ÿ’ก *Fix:* {suggestion}\")\n", + " output.append(\"\")\n", + " \n", + " return \"\\n\".join(output)\n", + "\n", + "def format_improvements_output(result: Dict) -> str:\n", + " \"\"\"Format improvement suggestions for display.\"\"\"\n", + " if not result.get(\"success\"):\n", + " return f\"**Error with {result['model']}:** {result.get('error', 'Unknown error')}\"\n", + " \n", + " improvements = result.get(\"improvements\", [])\n", + " if not improvements:\n", + " return f\"โœ… **{result['model']}**: Code follows best practices. No major improvements needed!\"\n", + " \n", + " output = [f\"**{result['model']}** - {len(improvements)} suggestion(s):\\n\"]\n", + " \n", + " for imp in improvements:\n", + " category = imp.get(\"category\", \"general\").replace(\"_\", \" \").title()\n", + " line = imp.get(\"line\", \"?\")\n", + " current = imp.get(\"current\", \"\")\n", + " improved = imp.get(\"improved\", \"\")\n", + " explanation = imp.get(\"explanation\", \"\")\n", + " \n", + " output.append(f\"\\n๐Ÿ“ **{category}** (Line {line}):\")\n", + " if current and improved:\n", + " output.append(f\" Before: `{current[:60]}{'...' if len(current) > 60 else ''}`\")\n", + " output.append(f\" After: `{improved[:60]}{'...' if len(improved) > 60 else ''}`\")\n", + " if explanation:\n", + " output.append(f\" ๐Ÿ’ก {explanation}\")\n", + " \n", + " return \"\\n\".join(output)\n", + "\n", + "def format_tests_output(result: Dict) -> str:\n", + " \"\"\"Format test generation results for display.\"\"\"\n", + " if not result.get(\"success\"):\n", + " return f\"**Error with {result['model']}:** {result.get('error', 'Unknown error')}\"\n", + " \n", + " test_code = result.get(\"test_code\", \"\")\n", + " if not test_code:\n", + " return f\"โš ๏ธ **{result['model']}**: No tests generated.\"\n", + " \n", + " return test_code\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def review_code(code: str, model_key: str, include_tests: bool = True) -> tuple:\n", + " \"\"\"Main function to perform complete code review.\"\"\"\n", + " if not code.strip():\n", + " return \"Please provide code to review.\", \"\", \"\"\n", + " \n", + " # Detect bugs\n", + " bugs_result = detect_bugs(code, model_key)\n", + " bugs_output = format_bugs_output(bugs_result)\n", + " bugs_issues = bugs_result.get(\"issues\", [])\n", + " \n", + " # Suggest improvements\n", + " improvements_result = suggest_improvements(code, model_key)\n", + " improvements_output = format_improvements_output(improvements_result)\n", + " \n", + " # Generate tests\n", + " tests_output = \"\"\n", + " if include_tests:\n", + " tests_result = generate_tests(code, bugs_issues, model_key)\n", + " tests_output = format_tests_output(tests_result)\n", + " \n", + " return bugs_output, improvements_output, tests_output\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "def compare_models(code: str, model_keys: List[str]) -> str:\n", + " \"\"\"Compare multiple models on the same code.\"\"\"\n", + " if not code.strip():\n", + " return \"Please provide code to review.\"\n", + " \n", + " results = []\n", + " all_issues = []\n", + " \n", + " for model_key in model_keys:\n", + " result = detect_bugs(code, model_key)\n", + " results.append(result)\n", + " if result.get(\"success\"):\n", + " all_issues.extend(result.get(\"issues\", []))\n", + " \n", + " # Build comparison output\n", + " output = [\"# Model Comparison Results\\n\"]\n", + " \n", + " for result in results:\n", + " model_name = result[\"model\"]\n", + " issues = result.get(\"issues\", [])\n", + " success = result.get(\"success\", False)\n", + " \n", + " if success:\n", + " output.append(f\"\\n**{model_name}**: Found {len(issues)} issue(s)\")\n", + " if issues:\n", + " severity_counts = {}\n", + " for issue in issues:\n", + " sev = issue.get(\"severity\", \"low\")\n", + " severity_counts[sev] = severity_counts.get(sev, 0) + 1\n", + " output.append(f\" Breakdown: {dict(severity_counts)}\")\n", + " else:\n", + " output.append(f\"\\n**{model_name}**: Error - {result.get('error', 'Unknown')}\")\n", + " \n", + " # Find consensus issues (found by multiple models)\n", + " if len(results) > 1:\n", + " issue_signatures = {}\n", + " for result in results:\n", + " if result.get(\"success\"):\n", + " for issue in result.get(\"issues\", []):\n", + " # Create signature from line and issue description\n", + " sig = f\"{issue.get('line')}-{issue.get('issue', '')[:50]}\"\n", + " if sig not in issue_signatures:\n", + " issue_signatures[sig] = []\n", + " issue_signatures[sig].append(result[\"model\"])\n", + " \n", + " consensus = [sig for sig, models in issue_signatures.items() if len(models) > 1]\n", + " if consensus:\n", + " output.append(f\"\\n\\n **Consensus Issues**: {len(consensus)} issue(s) identified by multiple models\")\n", + " \n", + " return \"\\n\".join(output)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Gradio UI\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "* Running on local URL: http://127.0.0.1:7883\n", + "* To create a public link, set `share=True` in `launch()`.\n" + ] + }, + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Example buggy code for testing\n", + "EXAMPLE_CODE = '''def divide_numbers(a, b):\n", + " return a / b\n", + "\n", + "def process_user_data(user_input):\n", + " # Missing input validation\n", + " result = eval(user_input)\n", + " return result\n", + "\n", + "def get_user_by_id(user_id):\n", + " # SQL injection vulnerability\n", + " query = f\"SELECT * FROM users WHERE id = {user_id}\"\n", + " return query\n", + "\n", + "def calculate_average(numbers):\n", + " total = sum(numbers)\n", + " return total / len(numbers) # Potential division by zero\n", + "'''\n", + "\n", + "def create_ui():\n", + " with gr.Blocks(title=\"AI Code Review Assistant\", theme=gr.themes.Soft()) as demo:\n", + " gr.Markdown(\"\"\"\n", + " # ๐Ÿ” AI-Powered Code Review Assistant\n", + " \n", + " Review your Python code using open-source AI models. Detect bugs, get improvement suggestions, and generate unit tests.\n", + " \"\"\")\n", + " \n", + " with gr.Row():\n", + " with gr.Column(scale=2):\n", + " code_input = gr.Code(\n", + " label=\"Python Code to Review\",\n", + " value=EXAMPLE_CODE,\n", + " language=\"python\",\n", + " lines=20\n", + " )\n", + " \n", + " with gr.Row():\n", + " model_selector = gr.Dropdown(\n", + " choices=list(MODELS.keys()),\n", + " value=list(MODELS.keys())[0],\n", + " label=\"Select Model\",\n", + " info=\"Choose an open-source code model\"\n", + " )\n", + " \n", + " include_tests = gr.Checkbox(\n", + " label=\"Generate Tests\",\n", + " value=True\n", + " )\n", + " \n", + " with gr.Row():\n", + " review_btn = gr.Button(\"๐Ÿ” Review Code\", variant=\"primary\", scale=2)\n", + " compare_btn = gr.Button(\"๐Ÿ“Š Compare Models\", variant=\"secondary\", scale=1)\n", + " \n", + " with gr.Column(scale=3):\n", + " with gr.Tabs() as tabs:\n", + " with gr.Tab(\"๐Ÿ› Bug Detection\"):\n", + " bugs_output = gr.Markdown(value=\"Select a model and click 'Review Code' to analyze your code.\")\n", + " \n", + " with gr.Tab(\"โœจ Improvements\"):\n", + " improvements_output = gr.Markdown(value=\"Get suggestions for code improvements and best practices.\")\n", + " \n", + " with gr.Tab(\"๐Ÿงช Unit Tests\"):\n", + " tests_output = gr.Code(\n", + " label=\"Generated Test Code\",\n", + " language=\"python\",\n", + " lines=25\n", + " )\n", + " \n", + " with gr.Tab(\"๐Ÿ“Š Comparison\"):\n", + " comparison_output = gr.Markdown(value=\"Compare multiple models side-by-side.\")\n", + " \n", + " # Event handlers\n", + " review_btn.click(\n", + " fn=review_code,\n", + " inputs=[code_input, model_selector, include_tests],\n", + " outputs=[bugs_output, improvements_output, tests_output]\n", + " )\n", + " \n", + " def compare_selected_models(code):\n", + " # Compare first 3 models by default\n", + " model_keys = list(MODELS.keys())[:3]\n", + " return compare_models(code, model_keys)\n", + " \n", + " compare_btn.click(\n", + " fn=compare_selected_models,\n", + " inputs=[code_input],\n", + " outputs=[comparison_output]\n", + " )\n", + " \n", + " gr.Examples(\n", + " examples=[\n", + " [EXAMPLE_CODE],\n", + " [\"\"\"def fibonacci(n):\n", + " if n <= 1:\n", + " return n\n", + " return fibonacci(n-1) + fibonacci(n-2)\n", + "\"\"\"],\n", + " [\"\"\"def parse_config(file_path):\n", + " with open(file_path) as f:\n", + " return eval(f.read())\n", + "\"\"\"]\n", + " ],\n", + " inputs=[code_input]\n", + " )\n", + " \n", + " return demo\n", + "\n", + "demo = create_ui()\n", + "demo.launch(inbrowser=True, share=False)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "**Qwen2.5-Coder 32B (OpenRouter)** - Found 2 issue(s):\n", + "\n", + "๐Ÿ”ด **CRITICAL** (Line 2): No division by zero protection\n", + " ๐Ÿ’ก *Fix:* Add a check for b == 0 and raise ValueError or handle ZeroDivisionError\n", + "\n", + "๐ŸŸก **MEDIUM** (Line 2): No input validation for numeric types\n", + " ๐Ÿ’ก *Fix:* Add type checking to ensure a and b are numbers (int/float)\n", + "\n" + ] + } + ], + "source": [ + "# Test with a simple example\n", + "test_code = \"\"\"def divide(a, b):\n", + " return a / b\n", + "\"\"\"\n", + "\n", + "# Test bug detection\n", + "result = detect_bugs(test_code, list(MODELS.keys())[0])\n", + "print(format_bugs_output(result))\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 288165922cfdd46026d44c38aaa31204f889f88a Mon Sep 17 00:00:00 2001 From: Philip Omoigui Date: Sun, 2 Nov 2025 13:41:22 +0100 Subject: [PATCH 2/2] Updated System message for unit tests generation --- .../philip/week4_EXERCISE.ipynb | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/week4/community-contributions/philip/week4_EXERCISE.ipynb b/week4/community-contributions/philip/week4_EXERCISE.ipynb index 039e2e2..c6f0e82 100644 --- a/week4/community-contributions/philip/week4_EXERCISE.ipynb +++ b/week4/community-contributions/philip/week4_EXERCISE.ipynb @@ -60,14 +60,14 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Configured 3 models\n", + "Configured 2 models\n", "OpenRouter models available (perfect for limited storage demos!)\n" ] } @@ -126,7 +126,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -170,6 +170,7 @@ "- Tests for any bugs that were identified\n", "\n", "Return ONLY Python code with pytest tests. Include the original code at the top if needed.\n", + "Put the imports at the top of the file first.\n", "Do not include explanations or markdown formatting.\"\"\"\n" ] }, @@ -486,21 +487,21 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "* Running on local URL: http://127.0.0.1:7883\n", + "* Running on local URL: http://127.0.0.1:7884\n", "* To create a public link, set `share=True` in `launch()`.\n" ] }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -513,9 +514,47 @@ "data": { "text/plain": [] }, - "execution_count": 21, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Traceback (most recent call last):\n", + " File \"c:\\Users\\Philo Baba\\llm_engineering\\.venv\\Lib\\site-packages\\gradio\\queueing.py\", line 745, in process_events\n", + " response = await route_utils.call_process_api(\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"c:\\Users\\Philo Baba\\llm_engineering\\.venv\\Lib\\site-packages\\gradio\\route_utils.py\", line 354, in call_process_api\n", + " output = await app.get_blocks().process_api(\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"c:\\Users\\Philo Baba\\llm_engineering\\.venv\\Lib\\site-packages\\gradio\\blocks.py\", line 2116, in process_api\n", + " result = await self.call_function(\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"c:\\Users\\Philo Baba\\llm_engineering\\.venv\\Lib\\site-packages\\gradio\\blocks.py\", line 1623, in call_function\n", + " prediction = await anyio.to_thread.run_sync( # type: ignore\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"c:\\Users\\Philo Baba\\llm_engineering\\.venv\\Lib\\site-packages\\anyio\\to_thread.py\", line 56, in run_sync\n", + " return await get_async_backend().run_sync_in_worker_thread(\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"c:\\Users\\Philo Baba\\llm_engineering\\.venv\\Lib\\site-packages\\anyio\\_backends\\_asyncio.py\", line 2485, in run_sync_in_worker_thread\n", + " return await future\n", + " ^^^^^^^^^^^^\n", + " File \"c:\\Users\\Philo Baba\\llm_engineering\\.venv\\Lib\\site-packages\\anyio\\_backends\\_asyncio.py\", line 976, in run\n", + " result = context.run(func, *args)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"c:\\Users\\Philo Baba\\llm_engineering\\.venv\\Lib\\site-packages\\gradio\\utils.py\", line 915, in wrapper\n", + " response = f(*args, **kwargs)\n", + " ^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\Philo Baba\\AppData\\Local\\Temp\\ipykernel_43984\\2272281361.py\", line 7, in review_code\n", + " bugs_result = detect_bugs(code, model_key)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\Philo Baba\\AppData\\Local\\Temp\\ipykernel_43984\\638705449.py\", line 23, in detect_bugs\n", + " model_config = MODELS[model_key]\n", + " ~~~~~~^^^^^^^^^^^\n", + "KeyError: 'deepseek-coder-v2-openrouter'\n" + ] } ], "source": [