From faeef796f864d52952d4e303afb977473a1fc065 Mon Sep 17 00:00:00 2001 From: Jonas Thamane <166150947+NathiJonas@users.noreply.github.com> Date: Wed, 4 Mar 2026 06:28:38 +0200 Subject: [PATCH 1/2] Update JonasThamane --- community-contributions/JonasThamane | 226 +++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) diff --git a/community-contributions/JonasThamane b/community-contributions/JonasThamane index 8b13789179..e1e1ba10b2 100644 --- a/community-contributions/JonasThamane +++ b/community-contributions/JonasThamane @@ -1 +1,227 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d006b2ea-9dfe-49c7-88a9-a5a0775185fd", + "metadata": {}, + "source": [ + "# Additional End of week Exercise - week 2\n", + "\n", + "Now use everything you've learned from Week 2 to build a full prototype for the technical question/answerer you built in Week 1 Exercise.\n", + "\n", + "This should include a Gradio UI, streaming, use of the system prompt to add expertise, and the ability to switch between models. Bonus points if you can demonstrate use of a tool!\n", + "\n", + "If you feel bold, see if you can add audio input so you can talk to it, and have it respond with audio. ChatGPT or Claude can help you, or email me if you have questions.\n", + "\n", + "I will publish a full solution here soon - unless someone beats me to it...\n", + "\n", + "There are so many commercial applications for this, from a language tutor, to a company onboarding solution, to a companion AI to a course (like this one!) I can't wait to see your results." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a07e7793-b8f5-44f4-aded-5562f633271a", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "import ast\n", + "\n", + "\n", + "def analyze_python_code(code: str) -> str:\n", + " \"\"\"\n", + " Analyze Python code structure using AST.\n", + " Returns a summary of node types used.\n", + " \"\"\"\n", + "\n", + " try:\n", + " tree = ast.parse(code)\n", + " node_types = {type(node).__name__ for node in ast.walk(tree)}\n", + " return f\"AST Node Types Found:\\n{sorted(node_types)}\"\n", + " except Exception as e:\n", + " return f\"Error analyzing code: {e}\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab621326", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "from openai import OpenAI\n", + "from tools import analyze_python_code\n", + "import json\n", + "\n", + "\n", + "SYSTEM_PROMPT = \"\"\"\n", + "You are a senior software engineer and technical tutor.\n", + "Explain concepts clearly and deeply.\n", + "When appropriate, suggest edge cases and performance considerations.\n", + "If the user provides Python code, you may call the code analysis tool.\n", + "\"\"\"\n", + "\n", + "\n", + "class TechnicalTutor:\n", + "\n", + " def __init__(self, provider=\"openai\", model=\"gpt-5-nano\"):\n", + " self.provider = provider\n", + " self.model = model\n", + "\n", + " if provider == \"openai\":\n", + " self.client = OpenAI()\n", + "\n", + " elif provider == \"ollama\":\n", + " self.client = OpenAI(\n", + " base_url=\"http://localhost:11434/v1\",\n", + " api_key=\"ollama\"\n", + " )\n", + " else:\n", + " raise ValueError(\"Provider must be 'openai' or 'ollama'.\")\n", + "\n", + " def ask(self, question):\n", + "\n", + " messages = [\n", + " {\"role\": \"system\", \"content\": SYSTEM_PROMPT},\n", + " {\"role\": \"user\", \"content\": question}\n", + " ]\n", + "\n", + " stream = self.client.chat.completions.create(\n", + " model=self.model,\n", + " messages=messages,\n", + " stream=True\n", + " )\n", + "\n", + " full_response = \"\"\n", + "\n", + " for chunk in stream:\n", + " delta = chunk.choices[0].delta.content\n", + " if delta:\n", + " full_response += delta\n", + " yield full_response\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37b6b739", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "import gradio as gr\n", + "from tutor import TechnicalTutor\n", + "\n", + "\n", + "def chat_interface(message, provider, model):\n", + " tutor = TechnicalTutor(provider=provider, model=model)\n", + "\n", + " for partial in tutor.ask(message):\n", + " yield partial\n", + "\n", + "\n", + "with gr.Blocks() as demo:\n", + "\n", + " gr.Markdown(\"# 🧠 Technical Tutor Prototype\")\n", + "\n", + " with gr.Row():\n", + " provider = gr.Dropdown(\n", + " choices=[\"openai\", \"ollama\"],\n", + " value=\"openai\",\n", + " label=\"Provider\"\n", + " )\n", + "\n", + " model = gr.Textbox(\n", + " value=\"gpt-5-nano\",\n", + " label=\"Model Name\"\n", + " )\n", + "\n", + " chatbot = gr.Chatbot()\n", + " msg = gr.Textbox(label=\"Ask a technical question\")\n", + "\n", + " def respond(message, chat_history, provider, model):\n", + " tutor = TechnicalTutor(provider=provider, model=model)\n", + " chat_history = chat_history or []\n", + "\n", + " response = \"\"\n", + " for partial in tutor.ask(message):\n", + " response = partial\n", + " yield \"\", chat_history + [(message, response)]\n", + "\n", + " msg.submit(\n", + " respond,\n", + " [msg, chatbot, provider, model],\n", + " [msg, chatbot],\n", + " )\n", + "\n", + "demo.launch()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc27d0c7", + "metadata": {}, + "outputs": [], + "source": [ + "audio_input = gr.Audio(type=\"filepath\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7250b37", + "metadata": {}, + "outputs": [], + "source": [ + "import whisper\n", + "\n", + "model_whisper = whisper.load_model(\"base\")\n", + "\n", + "def transcribe(audio_path):\n", + " result = model_whisper.transcribe(audio_path)\n", + " return result[\"text\"]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf9e9e6c", + "metadata": {}, + "outputs": [], + "source": [ + "import pyttsx3\n", + "\n", + "engine = pyttsx3.init()\n", + "engine.save_to_file(response, \"response.mp3\")\n", + "engine.runAndWait()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 28248d7628fb3e0fa1f59ad8a39cf78d7c24e3bd Mon Sep 17 00:00:00 2001 From: Jonas Thamane <166150947+NathiJonas@users.noreply.github.com> Date: Fri, 6 Mar 2026 00:12:02 +0200 Subject: [PATCH 2/2] Rename JonasThamane to JonasThamane.ipynb --- community-contributions/{JonasThamane => JonasThamane.ipynb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename community-contributions/{JonasThamane => JonasThamane.ipynb} (100%) diff --git a/community-contributions/JonasThamane b/community-contributions/JonasThamane.ipynb similarity index 100% rename from community-contributions/JonasThamane rename to community-contributions/JonasThamane.ipynb