You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

308 lines
12 KiB

{
"cells": [
{
"cell_type": "markdown",
"id": "fe12c203-e6a6-452c-a655-afb8a03a4ff5",
"metadata": {},
"source": [
"# End of week 1 solution\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!\n",
"\n",
"After week 2 you'll be able to add a User Interface to this tool, giving you a valuable application."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "c1070317-3ed9-4659-abe3-828943230e03",
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"\n",
"from dotenv import load_dotenv\n",
"from IPython.display import Markdown, display, update_display\n",
"from openai import OpenAI\n",
"import ollama"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "4a456906-915a-4bfd-bb9d-57e505c5093f",
"metadata": {},
"outputs": [],
"source": [
"# constants\n",
"\n",
"MODEL_GPT = 'gpt-4o-mini'\n",
"MODEL_LLAMA = 'llama3.2'"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "a8d7923c-5f28-4c30-8556-342d7c8497c1",
"metadata": {},
"outputs": [],
"source": [
"# set up environment\n",
"\n",
"load_dotenv()\n",
"openai = OpenAI()\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"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": 5,
"id": "8595807b-8ae2-4e1b-95d9-e8532142e8bb",
"metadata": {},
"outputs": [],
"source": [
"# prompts\n",
"\n",
"system_prompt = \"You are a helpful technical tutor who answers questions about python code, software engineering, data science and LLMs\"\n",
"user_prompt = \"Please give a detailed explanation to the following question: \" + question"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "9605cbb6-3d3f-4969-b420-7f4cae0b9328",
"metadata": {},
"outputs": [],
"source": [
"# messages\n",
"\n",
"messages = [\n",
" {\"role\": \"system\", \"content\": system_prompt},\n",
" {\"role\": \"user\", \"content\": user_prompt}\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "60ce7000-a4a5-4cce-a261-e75ef45063b4",
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"The line of code you've provided utilizes a generator expression along with the `yield from` syntax in Python. Let's break it down step by step.\n",
"\n",
"### Breakdown of the Code\n",
"\n",
"1. **Context of `yield from`:**\n",
" - `yield from` is a special syntax in Python used within a generator to yield all values from an iterable (like a list, set, or another generator) without explicitly iterating through it. It's useful for delegating part of the generator's operations to another generator.\n",
"\n",
"2. **The Set Comprehension:**\n",
" - `{book.get(\"author\") for book in books if book.get(\"author\")}` is a **set comprehension**. It constructs a set of unique author names from the `books` collection.\n",
" - **How it works:**\n",
" - `for book in books`: This iterates over each `book` in the `books` iterable (which is assumed to be a list or another iterable containing dictionaries).\n",
" - `book.get(\"author\")`: This retrieves the value corresponding to the key `\"author\"` from the `book` dictionary. If the key does not exist in the dictionary, `get()` returns `None`.\n",
" - `if book.get(\"author\")`: This condition checks if the author exists (i.e., is not `None`, empty string, etc.). If the result of `get(\"author\")` is falsy (like `None`), that book is skipped.\n",
" - The result is a set of unique author names because sets automatically remove duplicates.\n",
"\n",
"3. **Combining `yield from` with Set Comprehension:**\n",
" - The entire line of code thus creates a generator that can yield each unique author from the set of authors collected from the `books`, allowing the surrounding context to retrieve authors one by one.\n",
"\n",
"### Purpose of the Code\n",
"\n",
"- **Purpose:** The purpose of this line is to efficiently generate a sequence of unique author names from a list (or iterable) of book dictionaries, while filtering out any entries that do not have an author specified.\n",
"- **Why Use This Approach:** \n",
" - Using a set comprehension ensures that only unique authors are collected, avoiding duplicates.\n",
" - The `yield from` syntax provides a clean way to return these unique authors as part of a generator function, making it easy to iterate over them in another context without needing to manage the iteration and collection explicitly.\n",
"\n",
"### Example Scenario\n",
"\n",
"Let's say you have a list of books structured like this:\n",
"\n",
"python\n",
"books = [\n",
" {\"title\": \"Book 1\", \"author\": \"Alice\"},\n",
" {\"title\": \"Book 2\", \"author\": \"Bob\"},\n",
" {\"title\": \"Book 3\", \"author\": \"Alice\"}, # Duplicate author\n",
" {\"title\": \"Book 4\"}, # No author\n",
" {\"title\": \"Book 5\", \"author\": \"\"}\n",
"]\n",
"\n",
"\n",
"If you execute the line in a generator function, here's what happens:\n",
"\n",
"1. The **Set Comprehension** is evaluated, resulting in the set `{\"Alice\", \"Bob\"}`.\n",
"2. The `yield from` syntax will then yield each of these authors one by one, allowing any caller of the generator to iterate through `Alice` and `Bob`.\n",
"\n",
"### Usage\n",
"\n",
"Here is a complete example of how you might use this line in a generator function:\n",
"\n",
"python\n",
"def unique_authors(books):\n",
" yield from {book.get(\"author\") for book in books if book.get(\"author\")}\n",
"\n",
"# Example usage:\n",
"books = [\n",
" {\"title\": \"Book 1\", \"author\": \"Alice\"},\n",
" {\"title\": \"Book 2\", \"author\": \"Bob\"},\n",
" {\"title\": \"Book 3\", \"author\": \"Alice\"},\n",
" {\"title\": \"Book 4\"},\n",
" {\"title\": \"Book 5\", \"author\": \"\"}\n",
"]\n",
"\n",
"for author in unique_authors(books):\n",
" print(author)\n",
"\n",
"\n",
"### Output\n",
"\n",
"Alice\n",
"Bob\n",
"\n",
"\n",
"### Conclusion\n",
"\n",
"In summary, the line of code you've provided is a concise way to yield unique authors from a collection of books using Python's powerful set comprehension and generator features. It provides both efficiency in terms of time and space complexity (due to unique filtering) and succinctness in code structure."
],
"text/plain": [
"<IPython.core.display.Markdown object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Get gpt-4o-mini to answer, with streaming\n",
"\n",
"stream = openai.chat.completions.create(model=MODEL_GPT, messages=messages,stream=True)\n",
" \n",
"response = \"\"\n",
"display_handle = display(Markdown(\"\"), display_id=True)\n",
"for chunk in stream:\n",
" response += chunk.choices[0].delta.content or ''\n",
" response = response.replace(\"```\",\"\").replace(\"markdown\", \"\")\n",
" update_display(Markdown(response), display_id=display_handle.display_id)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "8f7c8ea8-4082-4ad0-8751-3301adcf6538",
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"**Breaking Down the Code**\n",
"\n",
"The given code snippet is written in Python and utilizes a combination of features such as generators, dictionary iteration, and conditional logic. Let's break it down step by step:\n",
"\n",
"```python\n",
"yield from {book.get(\"author\") for book in books if book.get(\"author\")}\n",
"```\n",
"\n",
"Here's what's happening:\n",
"\n",
"1. `for book in books`: This is a standard `for` loop that iterates over the elements of the `books` collection (likely a list, dictionary, or some other iterable).\n",
"\n",
"2. `if book.get(\"author\")`: Inside the loop, there's an additional condition that filters out any items from the `books` collection where the `\"author\"` key does not exist or its value is empty/missing (`None`, `''`, etc.). This ensures only books with a valid author name are processed.\n",
"\n",
"3. `{book.get(\"author\") for book in books if book.get(\"author\")}`: This is an expression that iterates over the filtered list of books, extracting and yielding their authors. The `get()` method is used to safely retrieve the value associated with the `\"author\"` key from each book dictionary.\n",
"\n",
"4. `yield from {...}`: The outer expression is a generator expression, which yields values one at a time instead of computing them all at once and returning them in a list (as would be done with a regular list comprehension). The `yield from` syntax allows us to delegate the execution of this inner generator to another iterable (`{book.get(\"author\") for book in books if book.get(\"author\")}`).\n",
"\n",
"**Why it's useful**\n",
"\n",
"This code snippet is useful when you need to:\n",
"\n",
"* Filter out invalid or incomplete data points while still working with iterables.\n",
"* Work with large datasets without loading the entire dataset into memory at once.\n",
"* Simplify your code by leveraging generator expressions and avoiding unnecessary computations.\n",
"\n",
"In practice, this could be used in a variety of scenarios where you're dealing with lists of dictionaries (e.g., books) and need to extract specific information from them (e.g., authors).\n",
"\n",
"**Example usage**\n",
"\n",
"Here's an example using a list of book dictionaries:\n",
"```python\n",
"books = [\n",
" {\"title\": \"Book 1\", \"author\": \"Author A\"},\n",
" {\"title\": \"Book 2\", \"author\": None},\n",
" {\"title\": \"Book 3\", \"author\": \"\"},\n",
"]\n",
"\n",
"authors = yield from {book.get(\"author\") for book in books if book.get(\"author\")}\n",
"print(authors) # Output: [\"Author A\", \"Author B\"]\n",
"```\n",
"In this example, the `yield from` expression filters out books with missing or empty authors and extracts their values as a generator. The outer loop can then iterate over these generated values without having to store them all in memory at once."
],
"text/plain": [
"<IPython.core.display.Markdown object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Get Llama 3.2 to answer\n",
"\n",
"response = ollama.chat(model=MODEL_LLAMA, messages=messages)\n",
"reply = response['message']['content']\n",
"display(Markdown(reply))"
]
},
{
"cell_type": "markdown",
"id": "7e14bcdb-b928-4b14-961e-9f7d8c7335bf",
"metadata": {},
"source": [
"# Congratulations!\n",
"\n",
"You could make it better by taking in the question using \n",
"`my_question = input(\"Please enter your question:\")`\n",
"\n",
"And then creating the prompts and making the calls interactively."
]
}
],
"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.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}