{ "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": 38, "id": "c1070317-3ed9-4659-abe3-828943230e03", "metadata": {}, "outputs": [], "source": [ "# imports\n", "# If these fail, please check you're running from an 'activated' environment with (llms) in the command prompt\n", "\n", "import os\n", "import json\n", "import requests\n", "from dotenv import load_dotenv\n", "from openai import OpenAI\n", "import ollama" ] }, { "cell_type": "code", "execution_count": 41, "id": "4a456906-915a-4bfd-bb9d-57e505c5093f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "API key looks good so far\n" ] } ], "source": [ "# Initialize and constants\n", "\n", "# 📦 Load .env variables\n", "load_dotenv(override=True)\n", "# 🔐 Get OpenAI API key\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", "\n", "MODEL_GPT = 'gpt-4o-mini'\n", "MODEL_LLAMA = 'llama3.2'\n", "\n", "system_prompt = (\n", " \"You are a helpful assistant that explains technical concepts and code in a clear, simple way. \"\n", " \"Use bullet points and code examples when relevant. Always aim to make it beginner-friendly.\"\n", ")\n", "\n", "openai = OpenAI()\n" ] }, { "cell_type": "code", "execution_count": null, "id": "a8d7923c-5f28-4c30-8556-342d7c8497c1", "metadata": {}, "outputs": [], "source": [ "# set up environment\n" ] }, { "cell_type": "code", "execution_count": 36, "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", "# \"\"\"\n", "\n", "question = \"Explain closures in JavaScript.\"" ] }, { "cell_type": "code", "execution_count": 42, "id": "fa823e81", "metadata": {}, "outputs": [], "source": [ "# messages\n", "\n", "messages = [\n", " {\"role\": \"system\", \"content\": system_prompt},\n", " {\"role\": \"user\", \"content\": question}\n", "]" ] }, { "cell_type": "code", "execution_count": 43, "id": "60ce7000-a4a5-4cce-a261-e75ef45063b4", "metadata": {}, "outputs": [], "source": [ "# Get gpt-4o-mini to answer, with streaming\n", "def stream_gpt_answer(question):\n", " stream = openai.chat.completions.create(\n", " model=MODEL_GPT,\n", " messages=messages,\n", " 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", " display_handle.update(Markdown(response))" ] }, { "cell_type": "code", "execution_count": 44, "id": "d6c1f1c6", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "Certainly! Closures are a fundamental concept in JavaScript that can be a bit tricky to grasp at first, but they are very powerful. Here’s a simple breakdown:\n", "\n", "### What is a Closure?\n", "\n", "- A **closure** is a function that has access to its own scope, the scope of the outer function, and the global scope.\n", "- This means a closure can \"remember\" the environment in which it was created, even after that environment has finished executing.\n", "\n", "### How Closures Work\n", "\n", "1. **Function Inside a Function**: When you define a function inside another function, the inner function forms a closure with the outer function.\n", "2. **Access to Variables**: The inner function can access variables from the outer function (even after the outer function has executed).\n", "3. **Preserving State**: Closures allow you to maintain state in asynchronous operations or when dealing with events.\n", "\n", "### Example of a Closure\n", "\n", "Here’s a simple code example to illustrate closures:\n", "\n", "javascript\n", "function makeCounter() {\n", " let count = 0; // This variable is private to makeCounter\n", "\n", " return function() { // This inner function is a closure\n", " count += 1; // It can access the 'count' variable from the outer function\n", " return count;\n", " };\n", "}\n", "\n", "const counter1 = makeCounter();\n", "console.log(counter1()); // Output: 1\n", "console.log(counter1()); // Output: 2\n", "console.log(counter1()); // Output: 3\n", "\n", "const counter2 = makeCounter();\n", "console.log(counter2()); // Output: 1 (This is independent of counter1)\n", "\n", "\n", "### Breakdown of the Example\n", "\n", "- **Outer Function**: `makeCounter` defines a local variable `count`.\n", "- **Inner Function**: The inner function (returned by `makeCounter`) is a closure that can access `count`.\n", "- **Independence**: Each time you call `makeCounter`, it creates a new `count` variable, which means `counter1` and `counter2` maintain their own separate state.\n", "\n", "### Common Uses of Closures\n", "\n", "- **Data Privacy**: Encapsulating private data that cannot be accessed directly from outside the function.\n", "- **Partial Applications**: Pre-filling some arguments of a function.\n", "- **Callbacks**: Keeping track of variables in event handlers.\n", "\n", "### Key Points to Remember\n", "\n", "- Closures allow functions to maintain access to their lexical scope (the scope in which they were defined).\n", "- They help to create functions with private variables that are not accessible from outside.\n", "- Be mindful of memory usage; closures can lead to increased memory usage if not managed properly.\n", "\n", "### Conclusion\n", "\n", "Closures are an essential part of JavaScript that help create flexible and powerful programming patterns. Understanding them will improve your coding skills and help you write cleaner, more manageable code." ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "stream_gpt_answer(question)" ] }, { "cell_type": "code", "execution_count": 45, "id": "8f7c8ea8-4082-4ad0-8751-3301adcf6538", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "**What is a Closure?**\n", "\n", "A closure is a fundamental concept in programming, particularly in functional programming languages like JavaScript. It's a function that has access to its own scope and the scope of its outer functions, even when the outer functions have returned.\n", "\n", "**Why do Closures Matter?**\n", "\n", "Closures are useful for several reasons:\n", "\n", "* **Private variables**: Closures allow you to create private variables (i.e., variables that are not accessible from outside the closure).\n", "* **Function encapsulation**: Closures can be used to wrap a function and its related data, making it easier to reuse code.\n", "* **Memory management**: Closures help manage memory by preventing functions from being garbage collected prematurely.\n", "\n", "**How do Closures Work?**\n", "\n", "A closure is created when a function is defined inside another function. When the inner function is called, it has access to the variables of its outer function's scope, including any arguments passed to the outer function.\n", "\n", "Here's an example:\n", "```javascript\n", "function outerFunction() {\n", " let privateVariable = \"Hello, world!\";\n", " \n", " return function innerFunction() {\n", " console.log(privateVariable); // prints: Hello, world!\n", " console.log(this === outerFunction); // true\n", " }\n", "}\n", "\n", "const closure = outerFunction();\n", "closure(); // logs: Hello, world!, then undefined\n", "\n", "// accessing the private variable using the closure:\n", "console.log(closureprivateVariable); // \"Hello, world!\"\n", "```\n", "In this example:\n", "\n", "* `outerFunction` returns a new function (`innerFunction`) that has access to its own scope.\n", "* `innerFunction` has access to `outerFunction`'s variables (including `privateVariable`).\n", "* When the closure is called, it logs `privateVariable` and checks if it's equal to `outerFunction`.\n", "\n", "**Creating a Closure**\n", "\n", "To create a closure:\n", "\n", "1. Define an outer function.\n", "2. Define an inner function inside the outer function.\n", "3. Return the inner function from the outer function.\n", "\n", "Here's another example:\n", "```javascript\n", "function counter() {\n", " let count = 0;\n", " \n", " return function() {\n", " count += 1;\n", " console.log(count); // prints: 1, then 2, etc.\n", " }\n", "}\n", "\n", "const increment = counter();\n", "increment(); // logs: 1\n", "increment(); // logs: 2\n", "\n", "// creating a new closure:\n", "function nameGenerator(firstName, lastName) {\n", " return function() {\n", " console.log(`${firstName} ${lastName}`);\n", " }\n", "}\n", "\n", "const johnDoe = nameGenerator(\"John\", \"Doe\");\n", "johnDoe(); // logs: John Doe\n", "```\n", "In this example:\n", "\n", "* `counter` is an outer function that returns a new inner function.\n", "* The inner function increments a counter variable and logs its value.\n", "* A new closure (`nameGenerator`) is created with two parameters (first name and last name).\n", "\n", "**Real-world Applications of Closures**\n", "\n", "Closures have many practical applications, such as:\n", "\n", "* **Data hiding**: Private variables within closures can prevent external interference.\n", "* **Factory functions**: Closures can be used to create functions that produce different outputs based on input values.\n", "* **Event listeners**: Closures help manage event listeners by preventing them from being garbage collected prematurely.\n", "\n", "By understanding and using closures effectively, you can write more modular, efficient, and maintainable code." ], "text/plain": [ "" ] }, "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))" ] } ], "metadata": { "kernelspec": { "display_name": "llms", "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 }