From 34c6bb7084b16c99475bbc5dd72a175877c37f36 Mon Sep 17 00:00:00 2001 From: KB Date: Tue, 21 Oct 2025 08:58:26 -0400 Subject: [PATCH] kwabena_bootcamp --- .../kwabena/week1_exercise_solution.ipynb | 271 ++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 week1/community-contributions/kwabena/week1_exercise_solution.ipynb diff --git a/week1/community-contributions/kwabena/week1_exercise_solution.ipynb b/week1/community-contributions/kwabena/week1_exercise_solution.ipynb new file mode 100644 index 0000000..cb41f27 --- /dev/null +++ b/week1/community-contributions/kwabena/week1_exercise_solution.ipynb @@ -0,0 +1,271 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4ea14045", + "metadata": {}, + "source": [ + "# End of Week 1 Exercise\n", + "\n", + "In this exercise, I'm building a small tool that takes a technical question and gets an explanation from **two models** — one from OpenAI and one from Ollama. \n", + "The idea is to compare how they respond and understand how to use both APIs.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18d3787e", + "metadata": {}, + "outputs": [], + "source": [ + "# imports\n", + "# (following the style from Day 5)\n", + "\n", + "import os\n", + "from openai import OpenAI\n", + "from dotenv import load_dotenv\n", + "from IPython.display import Markdown, display\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1592e306", + "metadata": {}, + "outputs": [], + "source": [ + "# constants\n", + "\n", + "MODEL_GPT = \"gpt-4o-mini\"\n", + "MODEL_LLAMA = \"llama3.2\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "35da77ea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ API key loaded successfully\n" + ] + } + ], + "source": [ + "# set up environment\n", + "\n", + "load_dotenv(override=True)\n", + "api_key = os.getenv(\"OPENAI_API_KEY\")\n", + "\n", + "if not api_key:\n", + " print(\"⚠️ OPENAI_API_KEY not found in environment. Please add it to your .env file.\")\n", + "else:\n", + " print(\"✅ API key loaded successfully\")\n", + "\n", + "client = OpenAI(api_key=api_key)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "67efa212", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Question: Please explain what this code does and why:\n", + "yield from {book.get(\"author\") for book in books if book.get(\"author\")}\n", + "\n" + ] + } + ], + "source": [ + "# define the technical question\n", + "# (you can replace this text to ask something else)\n", + "\n", + "question = \"\"\"Please explain what this code does and why:\n", + "yield from {book.get(\"author\") for book in books if book.get(\"author\")}\n", + "\"\"\"\n", + "\n", + "print(\"Question:\", question)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "85e1ac5b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "🔹 GPT-4o-mini's answer:\n", + "\n", + "Certainly! The code you've provided is using the `yield from` statement in combination with a set comprehension to yield values from a collection. Let's break it down step by step:\n", + "\n", + "1. **Understanding `books`**: \n", + " - In this context, `books` is presumably a list (or another iterable) of dictionaries, where each dictionary represents a book. Each dictionary is expected to have an \"author\" key among potentially others.\n", + "\n", + "2. **Set Comprehension**:\n", + " - The expression `{book.get(\"author\") for book in books if book.get(\"author\")}` is a set comprehension. It iterates over each `book` in `books`.\n", + "\n", + " - `book.get(\"author\")` retrieves the value associated with the key \"author\" in the dictionary `book`. If the key \"author\" does not exist, `.get()` returns `None`.\n", + "\n", + " - The condition `if book.get(\"author\")` filters out books where the \"author\" key is either absent or has a `None` value. This ensures that only books with valid authors are considered.\n", + "\n", + " - The result of this comprehension is a set of unique authors from the provided `books`. A set inherently eliminates duplicate values, so if multiple books have the same author, this will only store that author once.\n", + "\n", + "3. **Using `yield from`**:\n", + " - The `yield from` statement is used to yield all values from an iterable (in this case, the set of authors) one by one from a generator function. It allows for a cleaner way to yield multiple values without having to loop through them manually.\n", + "\n", + "4. **Putting It All Together**:\n", + " - This line of code is typically found inside a generator function, and its purpose is to yield each unique author from the collection of books. As a result, when the generator is iterated over, each author will be returned in sequence.\n", + "\n", + "### Summary:\n", + "The provided code extracts unique authors from a list of book dictionaries and yields them one by one. Authors are obtained by checking for the presence of the \"author\" key, ensuring that duplicate authors are avoided. The overall effect is that when this generator is called, it produces a sequence of unique author names based on the input `books`.\n" + ] + } + ], + "source": [ + "# Get gpt-4o-mini to answer\n", + "\n", + "print(\"🔹 GPT-4o-mini's answer:\\n\")\n", + "\n", + "response = client.chat.completions.create(\n", + " model=MODEL_GPT,\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": \"You are a helpful Python tutor.\"},\n", + " {\"role\": \"user\", \"content\": question},\n", + " ],\n", + ")\n", + "\n", + "print(response.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "4c031d74", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "🔹 LLaMA 3.2's answer:\n", + "\n", + "Let's break down the given code:\n", + "\n", + "```python\n", + "yield from {book.get(\"author\") for book in books if book.get(\"author\")}\n", + "```\n", + "\n", + "**Explanation:**\n", + "\n", + "This line of code appears to be written in Python and utilizes a few advanced features.\n", + "\n", + "1. **Generator Expression**: `for book in books` is within an expression, denoted by curly braces `{}`. This makes it a generator expression.\n", + "2. **`yield from`**: The keyword \"yields from\" is used to delegate the computation of a sub-generator to another generator. It allows you to create a new iterable that yields values from inner generators.\n", + "\n", + "**Step-by-Step Breakdown:**\n", + "\n", + "1. `for book in books`: Iterate over each item (`book`) in an iterable collection (`books`).\n", + "2. `if book.get(\"author\")`: Check if the current `book` object has a key named `\"author\"` and it's not an empty string (thanks to `get()` method that returns `None` by default if the key doesn't exist).\n", + "3. `{...}`: This is a generator expression.\n", + "\n", + "**What happens when you run this code?**\n", + "\n", + "The `yield from {...}` part indicates that this generator will \"delegate\" its generation to another generator within the curly braces `{}`). In this case, it's simply an enumeration over a dictionary key (`'\"author\"'`), but what if we replace `\"author\"` with something like `{book.get(\"id\") for book in books if book.get(\"id\")}?\n", + "\n", + "The result is that `yield from {...}` will yield every unique value of a specific key (e.g., author's ID) within the dictionary keys, without directly accessing these values (`author` or `id`) themselves.\n", + "\n", + "**Use Case:**\n", + "\n", + "Imagine you are using this code to iterate over books by their \"author\"s. This would:\n", + "\n", + "1. Traverse through all books.\n", + "2. Filter out books with no authors (which are often empty strings due to data normalization).\n", + "\n", + "In the end, you can access each author's name (assuming `authors` dictionary like this `{author: \"John Doe\"}`) in a loop.\n", + "\n", + "**Example Code Snippet**\n", + "\n", + "```\n", + "def find_author(community_members):\n", + " for book in community_members:\n", + " yield from {book.get(\"author\") for book in books if book.get(\"author\")}\n", + " \n", + "books = [{\"author\": \"Jane Smith\"}, {\"author\": \"\"}, {\"id\": \"B123\", \"author\": \"Bob Johnson\"}]\n", + "author_names = list(find_author(community_members))\n", + "print(author_names) # Output: [\"Jane Smith\", \"Bob Johnson\"]\n", + "```\n", + "\n", + "Now, you will know how to \"yield from\" the dictionaries' values using `yield from`!\n" + ] + } + ], + "source": [ + "# Get LLaMA 3.2 to answer via local Ollama endpoint\n", + "\n", + "print(\"\\n🔹 LLaMA 3.2's answer:\\n\")\n", + "\n", + "ollama_client = OpenAI(base_url=\"http://localhost:11434/v1\",api_key=\"ollama\")\n", + "\n", + "response = ollama_client.chat.completions.create(\n", + " model=MODEL_LLAMA,\n", + " messages=[\n", + " {\"role\":\"system\",\"content\":\"You are a helpful AI tutor.\"},\n", + " {\"role\":\"user\",\"content\":question}\n", + " ],\n", + ")\n", + "\n", + "print(response.choices[0].message.content)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "e4ddf582", + "metadata": {}, + "source": [ + "### Reflection\n", + "\n", + "Both models provide explanations, but often with slightly different tones. \n", + "`gpt-4o-mini` tends to give more structured explanations, while `llama3.2` (running locally through Ollama) may be more concise or technical depending on its settings.\n", + "\n", + "This exercise helped me understand:\n", + "- How to send prompts and handle responses (including streaming).\n", + "- How easy it is to swap between OpenAI and local models.\n", + "- The value of comparing model outputs side by side.\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": 5 +}