diff --git a/community-contributions/dkisselev-zz/week1/week1 EXERCISE.ipynb b/community-contributions/dkisselev-zz/week1/week1 EXERCISE.ipynb new file mode 100644 index 0000000..ef18461 --- /dev/null +++ b/community-contributions/dkisselev-zz/week1/week1 EXERCISE.ipynb @@ -0,0 +1,266 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fe12c203-e6a6-452c-a655-afb8a03a4ff5", + "metadata": {}, + "source": [ + "# End of week 1 exercise\n", + "\n", + "To demonstrate your familiarity with OpenAI API, and also Ollama, build a tool that takes a technical question, \n", + "and responds with an explanation. This is a tool that you will be able to use yourself during the course!" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c1070317-3ed9-4659-abe3-828943230e03", + "metadata": {}, + "outputs": [], + "source": [ + "# imports\n", + "import os\n", + "import json\n", + "from dotenv import load_dotenv\n", + "from IPython.display import Markdown\n", + "from openai import OpenAI" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4a456906-915a-4bfd-bb9d-57e505c5093f", + "metadata": {}, + "outputs": [], + "source": [ + "# constants\n", + "\n", + "MODEL_GPT = 'gpt-4o-mini'\n", + "MODEL_LLAMA = 'llama3.2'\n", + "OLLAMA_BASE_URL = \"http://localhost:11434/v1\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a8d7923c-5f28-4c30-8556-342d7c8497c1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "API key looks good so far\n" + ] + } + ], + "source": [ + "# set up environment\n", + "\n", + "load_dotenv(override=True)\n", + "api_key = os.getenv('OPENAI_API_KEY')\n", + "\n", + "if api_key and api_key.startswith('sk-proj-') and len(api_key)>10:\n", + " print(\"API key looks good so far\")\n", + "else:\n", + " print(\"There might be a problem with your API key? Please visit the troubleshooting notebook!\")\n", + " \n", + "openai = OpenAI()\n", + "ollama = OpenAI(base_url=OLLAMA_BASE_URL, api_key='ollama')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3f0d0137-52b0-47a8-81a8-11a90a010798", + "metadata": {}, + "outputs": [], + "source": [ + "# here is the question; type over this to ask something new\n", + "\n", + "question = \"\"\"\n", + "Please explain what this code does and why:\n", + "yield from {book.get(\"author\") for book in books if book.get(\"author\")}\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "df0d958f", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt = \"\"\"You are individual that possesses a unique\n", + "and highly valuable combination of deep technical\n", + "expertise and excellent communication skills.\n", + "You grasp complex, specialized concepts and then distill\n", + "them into simple, understandable terms for people without\n", + "the same technical background.\n", + "\n", + "Present your answer as markdown\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "13506dd4", + "metadata": {}, + "outputs": [], + "source": [ + "messages = [\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": question}\n", + " ]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "60ce7000-a4a5-4cce-a261-e75ef45063b4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "Certainly! Let's break down the code you've provided:\n", + "\n", + "### Code Explanation\n", + "\n", + "```python\n", + "yield from {book.get(\"author\") for book in books if book.get(\"author\")}\n", + "```\n", + "\n", + "#### Components of the Code:\n", + "\n", + "1. **Set Comprehension**:\n", + " - `{book.get(\"author\") for book in books if book.get(\"author\")}`: This part of the code is a **set comprehension**. It creates a set of authors by iterating through a collection called `books`.\n", + " - `book.get(\"author\")`: This method retrieves the value associated with the key `\"author\"` from each `book` dictionary. If the specified key does not exist, `None` is returned.\n", + " - `if book.get(\"author\")`: This condition filters out any authors that are `None` or any falsy value, ensuring that only valid authors are included in the set.\n", + "\n", + "2. **Yielding Values**:\n", + " - `yield from`: This keyword is used in Python to yield values from a generator or an iterable. In this context, it takes the set created from the set comprehension and yields each author one at a time.\n", + "\n", + "### What the Code Does\n", + "\n", + "- The code iterates over a collection of `books`, each of which is expected to be a dictionary. For each book, it checks if the `\"author\"` key exists and has a valid value (not `None` or falsy).\n", + "- It collects all unique authors into a set to ensure no duplicates are included. The use of a set here also makes looking up authors efficient.\n", + "- Finally, it yields each author one at a time, allowing the consuming code to process each author individually.\n", + "\n", + "### Why Use This Code?\n", + "\n", + "- **Unique Values**: Using a set guarantees that each author is only yielded once, even if multiple books have the same author.\n", + "- **Memory Efficiency**: Using `yield from` allows handling potentially large collections of authors efficiently without having to store them all in memory at once.\n", + "- **Ease of Use**: This pattern is useful for generating a stream of results where the number of authors is not known in advance, and the consumer can process each author as it arrives.\n", + "\n", + "### Summary\n", + "\n", + "This code is effective for extracting unique authors from a list of book dictionaries, yielding each author for further processing without duplicates and in an efficient manner." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Get gpt-4o-mini to answer, with streaming\n", + "\n", + "response = openai.chat.completions.create(model=MODEL_GPT, messages=messages)\n", + "result = response.choices[0].message.content\n", + "display(Markdown(result))\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "8f7c8ea8-4082-4ad0-8751-3301adcf6538", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "**Understanding Recursive Yield**\n", + "\n", + "```python\n", + "yield from {book.get(\"author\") for book in books if book.get(\"author\")}\n", + "```\n", + "is a piece of Python code that utilizes the `yield from` syntax to delegate iterations over a sub-generators. This allows us to generate an iterable sequence one step at a time, avoiding memory overhead.\n", + "\n", + "### Breakdown\n", + "\n", + "* `{book.get(\"author\") for book in books if book.get(\"author\")}` is a generator expression:\n", + " * It defines a small, anonymous function (lambda-like) that takes `books` as input.\n", + " * For each item `book` in `books`, it calls another function (`get`) to retrieve the `\"author\"` attribute from an object.\n", + " * The resulting values are generated on-the-fly and sent through the generator expression.\n", + "* `yield from …`: The outer expression uses `yield from` to delegate control over iterations to the inner nested generator.\n", + " * Instead of dealing with each iterable individually, `yield from` sends its arguments down into the first argument, which it executes and yields values on.\n", + "\n", + "**Equivalent Code**\n", + "\n", + "Here is an equivalent version of this code using traditional loops:\n", + "\n", + "```python\n", + "def book_authors(books):\n", + " authors = []\n", + " for book in books:\n", + " if \"author\" in book:\n", + " author_name = book[\"author\"]\n", + " yield from (author_name,)\n", + " authors.append(author_name)\n", + "\n", + "# To use this function with our recursive yield version \n", + "# you can use it like so\n", + "for author_name in book_authors(books):\n", + " # Use this here\n", + "```\n", + "\n", + "### Why is Yield From useful?\n", + "\n", + "In programming languages that natively don't support nested generator iterations (like CPython), using `yield from` allows for elegant implementation of cooperative multitasking. It also simplifies the logic of writing generators.\n", + "\n", + "Note: However, all such recursive-yield constructions rely on Python’s behavior towards generator expressions in its specific use-case." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Get Llama 3.2 to answer\n", + "\n", + "response = ollama.chat.completions.create(model=MODEL_LLAMA, messages=messages)\n", + "result = response.choices[0].message.content\n", + "display(Markdown(result))" + ] + } + ], + "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.11.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}