{ "cells": [ { "cell_type": "markdown", "id": "8ec2fef2", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Using the OpenAI API\n", "* **Created by:** Eric Martinez\n", "* **For:** Software Engineering 2\n", "* **At:** University of Texas Rio-Grande Valley" ] }, { "cell_type": "markdown", "id": "6c3f79e3", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## OpenAI API\n", "\n", "The OpenAI API provides access to powerful LLMs like GPT-3.5 and GPT-4, enabling developers to leverage these models in their applications. To access the API, sign up for an API key on the OpenAI website and follow the documentation to make API calls.\n", "\n", "For enterprise: Azure OpenAI offers a robust and scalable platform for deploying LLMs in enterprise applications. It provides features like security, compliance, and support, making it an ideal choice for businesses looking to leverage LLMs.\n", "\n", "Options:\n", "* [[Free] Sign-up for access to my OpenAI service](https://platform.openai.com/signup) - _requires your UTRGV email and student ID_\n", "* [[Paid] Alternatively, sign-up for OpenAI API Access](https://platform.openai.com/signup)" ] }, { "cell_type": "markdown", "id": "c412a4e9", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Managing Application Secrets\n", "\n", "Secrets are sensitive information, such as API keys, passwords, or cryptographic keys, that must be protected to ensure the security and integrity of a system.\n", "\n", "In software development, secrets are often used to authenticate users, grant access to resources, or encrypt/decrypt data. Mismanaging or exposing secrets can lead to severe security breaches and data leaks." ] }, { "cell_type": "markdown", "id": "2059552f", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### Common examples of secrets\n", "* API keys\n", "* Database credentials\n", "* SSH keys\n", "* OAuth access tokens\n", "* Encryption/decryption keys" ] }, { "cell_type": "markdown", "id": "a1e650f8", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### Common mistakes when handling secrets\n", "* Storing secrets in plain text\n", "* Hardcoding secrets in source code\n", "* Sharing secrets through unsecured channels (e.g., email or messaging apps)\n", "* Using the same secret for multiple purposes\n", "* Not rotating or updating secrets regularly" ] }, { "cell_type": "markdown", "id": "66de4ac1", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "How attackers might obtain secrets\n", "* Exploiting vulnerabilities in software or infrastructure\n", "* Intercepting unencrypted communications\n", "* Gaining unauthorized access to repositories or storage systems\n", "* Social engineering or phishing attacks\n", "* Brute-forcing weak secrets\n", "\n", "The impact of compromised secrets\n", "* Unauthorized access to sensitive data\n", "* Data breaches, resulting in financial loss and reputational damage\n", "* Loss of trust from customers and stakeholders\n", "* Legal repercussions and regulatory fines\n", "* Potential takeover or manipulation of systems\n", "\n", "Steps to protect secrets\n", "* Store secrets securely using secret management tools or dedicated secret storage services\n", "* Encrypt secrets at rest and in transit\n", "* Use strong, unique secrets and rotate them regularly\n", "* Limit access to secrets on a need-to-know basis\n", "* Implement proper auditing and monitoring of secret usage\n", "\n", "Cloud services and secret management\n", "* Many cloud providers offer secret management services (e.g., AWS Secrets Manager, Azure Key Vault, Google Cloud Secret Manager) that securely store, manage, and rotate secrets.\n", "* These services often provide access control, encryption, and auditing capabilities.\n", "* Integrating cloud secret management services with your application can help secure sensitive information and reduce the risk of exposure.\n", "\n", "Best practices for secrets\n", "* Use different secrets for development, testing, and production environments to minimize the risk of accidental exposure.\n", "* Regularly audit and review secret access to ensure only authorized users have access.\n", "* Establish a clear process for managing secrets, including secret creation, storage, rotation, and deletion.\n", "* Educate team members on the importance of secret security and the best practices for handling sensitive information." ] }, { "cell_type": "markdown", "id": "ef366b65", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### Using `.dotenv` library to protect secrets in Python" ] }, { "cell_type": "markdown", "id": "dc39df10", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " `.dotenv` is a Python library that allows developers to load environment variables from a `.env` file. It helps keep secrets out of source code and makes it easier to manage and update them." ] }, { "cell_type": "markdown", "id": "ae0500ea", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Install the `python-dotenv` library" ] }, { "cell_type": "code", "execution_count": 45, "id": "1212333f", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "!pip -q install --upgrade python-dotenv" ] }, { "cell_type": "markdown", "id": "faecedf0", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Create a `.env` file in this folder using any editor." ] }, { "cell_type": "markdown", "id": "a880382a", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Add secrets as key-value pairs in the `.env` file" ] }, { "cell_type": "markdown", "id": "04f00703", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "If you are using my OpenAI service use the following format:" ] }, { "cell_type": "code", "execution_count": null, "id": "45b82889", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "OPENAI_API_BASE=\n", "OPENAI_API_KEY=" ] }, { "cell_type": "markdown", "id": "a952b103", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "If you are not using my OpenAI service then use the following format:" ] }, { "cell_type": "code", "execution_count": null, "id": "7cf6ed7b", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "OPENAI_API_KEY=" ] }, { "cell_type": "markdown", "id": "955963ed", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Then, use the following code to load those secrets into this notebook:" ] }, { "cell_type": "code", "execution_count": 47, "id": "fcadf45e", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from dotenv import load_dotenv\n", "\n", "load_dotenv() # take environment variables from .env." ] }, { "cell_type": "markdown", "id": "d3b6c394", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### Install Dependencies" ] }, { "cell_type": "code", "execution_count": 1, "id": "bcc79375", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "!pip -q install --upgrade openai" ] }, { "cell_type": "markdown", "id": "f2ed966d", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### Let's make a function to wrap OpenAI functionality and write some basic tests" ] }, { "cell_type": "markdown", "id": "c1b09026", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Start by simply seeing if we can make an API call" ] }, { "cell_type": "code", "execution_count": 50, "id": "0abdd4e9", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello! How can I assist you today?\n" ] } ], "source": [ "import openai\n", "from dotenv import load_dotenv\n", "\n", "load_dotenv() # take environment variables from .env.\n", "\n", "model=\"gpt-3.5-turbo\"\n", "messages=[{\"role\": \"user\", \"content\": \"hello\"}]\n", "\n", "completion = openai.ChatCompletion.create(model=model, messages=messages)\n", "\n", "print(completion.choices[0].message.content)" ] }, { "cell_type": "markdown", "id": "3f5d1530", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Great! Now let's wrap that in a function" ] }, { "cell_type": "code", "execution_count": 19, "id": "af4895c3", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello! How can I assist you today?\n" ] } ], "source": [ "import openai\n", "from dotenv import load_dotenv\n", "\n", "load_dotenv() # take environment variables from .env.\n", "\n", "def get_ai_reply(model=\"gpt-3.5-turbo\", user_message=\"\"):\n", " messages=[{\"role\": \"user\", \"content\": user_message}]\n", " completion = openai.ChatCompletion.create(model=model, messages=messages)\n", " return completion.choices[0].message.content\n", "\n", "print(get_ai_reply(user_message=\"hello\"))" ] }, { "cell_type": "markdown", "id": "f4506fe8", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Let's add some tests!\n", "\n", "These are traditional type of tests.\n", "\n", "Since the output is non-deterministic, generally, what are some things that we could test?\n", "\n", "At the very least, maybe that the output is a string?" ] }, { "cell_type": "code", "execution_count": 20, "id": "42a36d3a", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "import openai\n", "from dotenv import load_dotenv\n", "\n", "load_dotenv() # take environment variables from .env.\n", "\n", "def get_ai_reply(message, model=\"gpt-3.5-turbo\"): \n", " messages=[{\"role\": \"user\", \"content\": message}]\n", " \n", " completion = openai.ChatCompletion.create(model=model, messages=messages, temperature=temperature)\n", " return completion.choices[0].message.content\n", "\n", "# traditional tests\n", "assert isinstance(get_ai_reply(\"hello\"), str)\n", "assert isinstance(get_ai_reply(\"hello\", model=\"gpt-3.5-turbo\"), str)" ] }, { "cell_type": "markdown", "id": "ed166c5b", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "But what if we do what to test the output of the LLM?\n", "\n", "Is there anyway to control, atleast to some degree, the level of non-determinism?\n", "\n", "Yes! Let's add a temperature parameter, this will help us control the 'creativity' and 'randomness' of the response.\n", "\n", "Setting it to 0 helps ensure outputs are more consistent when given the same input.\n", "\n", "Valid values for temperature are between 0 and 1, inclusive.\n", "\n", "This will help us when writing tests, but is something that we should keep in mind that if we write tests against the LLM output, we might get the expect results _ONLY_ at low temperature.\n", "\n", "An ideal test strategy should resemble the temperature setting we will use in production." ] }, { "cell_type": "code", "execution_count": 31, "id": "9c03b774", "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "import openai\n", "from dotenv import load_dotenv\n", "\n", "load_dotenv() # take environment variables from .env.\n", "\n", "def get_ai_reply(message, model=\"gpt-3.5-turbo\", temperature=0): \n", " messages=[{\"role\": \"user\", \"content\": message}]\n", " \n", " completion = openai.ChatCompletion.create(model=model, messages=messages, temperature=temperature)\n", " return completion.choices[0].message.content\n", "\n", "# traditional tests\n", "assert isinstance(get_ai_reply(\"hello\"), str)\n", "assert isinstance(get_ai_reply(\"hello\", model=\"gpt-3.5-turbo\"), str)" ] }, { "cell_type": "code", "execution_count": null, "id": "15e09750", "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "If we run this enough times we should see that the output for the bottom run is more inconsistent." ] }, { "cell_type": "code", "execution_count": 33, "id": "f5ed24da", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello! How can I assist you today?\n", "Hello there! How can I assist you today?\n" ] } ], "source": [ "print(get_ai_reply(\"hello\")) # uses default of temperature 0\n", "print(get_ai_reply(\"hello\", temperature=0.9)) # uses default of temperature 0" ] }, { "cell_type": "markdown", "id": "ee320648", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Ok great! Now, an LLM is no good to us if we can't _steer_ it.\n", "\n", "So let's add the ability to input a _prompt_ or _system message_." ] }, { "cell_type": "code", "execution_count": 37, "id": "295839a5", "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "import openai\n", "from dotenv import load_dotenv\n", "\n", "load_dotenv() # take environment variables from .env.\n", "\n", "# Define a function to get the AI's reply using the OpenAI API\n", "def get_ai_reply(message, model=\"gpt-3.5-turbo\", system_message=None, temperature=0):\n", " # Initialize the messages list\n", " messages = []\n", " \n", " # Add the system message to the messages list\n", " if system_message is not None:\n", " messages += [{\"role\": \"system\", \"content\": system_message}]\n", " \n", " # Add the user's message to the messages list\n", " messages += [{\"role\": \"user\", \"content\": message}]\n", " \n", " # Make an API call to the OpenAI ChatCompletion endpoint with the model and messages\n", " completion = openai.ChatCompletion.create(\n", " model=model,\n", " messages=messages,\n", " temperature=temperature\n", " )\n", " \n", " # Extract and return the AI's response from the API response\n", " return completion.choices[0].message.content.strip()\n", "\n", "# traditional tests\n", "assert isinstance(get_ai_reply(\"hello\"), str)\n", "assert isinstance(get_ai_reply(\"hello\", model=\"gpt-3.5-turbo\"), str)" ] }, { "cell_type": "markdown", "id": "fc9763d6", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Let's see if we can get the LLM to follow instructions by adding instructions to the prompt.\n", "\n", "Run this cell a few times and see what happens. Is it consistent?" ] }, { "cell_type": "code", "execution_count": 36, "id": "c5e2a8b3", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "world.\n" ] } ], "source": [ "print(get_ai_reply(\"hello\", model=\"gpt-3.5-turbo\", system_message=\"When I say 'hello' you simply reply with 'world.'\"))" ] }, { "cell_type": "markdown", "id": "e59d3275", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "While the output is more or less controlled, the LLM responds with 'world.' or 'world'. While the word 'world' being in the string is pretty consistent, the punctuation is not.\n", "\n", "How do we write tests against this or have confidence with non-determinism?\n", "\n", "What is a test that we could write that:\n", "* would pass if the LLM outputs in a manner that is consistent with our expectations (and consistent with its own output)?\n", "* _we want to be true_ about our LLM system, and if it does not then we would want to know immediately and adjust our system?\n", "* if the prompt does not change, that our expectation holds true?\n", "* someone changes the prompt in a way that would break the rest of the system, that we would want to prevent that from being merged without fixing the downstream effects?" ] }, { "cell_type": "code", "execution_count": 38, "id": "d2b2bc15", "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "# non-deterministic tests\n", "system_message=\"When I say 'hello' you simply reply with 'world.'\"\n", "message=\"hello\"\n", "\n", "assert \"world\" in get_ai_reply(message, system_message=system_message)" ] }, { "cell_type": "markdown", "id": "5e17eefe", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Alright that worked!\n", "\n", "Now, let's extend the functionality to add the ability to pass message history so that it can have memory about what was said previously." ] }, { "cell_type": "code", "execution_count": 39, "id": "4fd88c05", "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "import openai\n", "from dotenv import load_dotenv\n", "\n", "load_dotenv() # take environment variables from .env.\n", "\n", "# Define a function to get the AI's reply using the OpenAI API\n", "def get_ai_reply(message, model=\"gpt-3.5-turbo\", system_message=None, temperature=0, message_history=[]):\n", " # Initialize the messages list\n", " messages = []\n", " \n", " # Add the system message to the messages list\n", " if system_message is not None:\n", " messages += [{\"role\": \"system\", \"content\": system_message}]\n", " \n", " # Add the message history to the messages list\n", " if message_history is not None:\n", " messages += message_history\n", " \n", " # Add the user's message to the messages list\n", " messages += [{\"role\": \"user\", \"content\": message}]\n", " \n", " # Make an API call to the OpenAI ChatCompletion endpoint with the model and messages\n", " completion = openai.ChatCompletion.create(\n", " model=model,\n", " messages=messages,\n", " temperature=temperature\n", " )\n", " \n", " # Extract and return the AI's response from the API response\n", " return completion.choices[0].message.content.strip()\n", "\n", "# traditional tests\n", "assert isinstance(get_ai_reply(\"hello\"), str)\n", "assert isinstance(get_ai_reply(\"hello\", model=\"gpt-3.5-turbo\"), str)\n", "\n", "# non-deterministic unit tests\n", "assert \"world\" in get_ai_reply(\"hello\", model=\"gpt-3.5-turbo\", system_message=\"When I say 'hello' you simply reply with 'world.'\")" ] }, { "cell_type": "markdown", "id": "e0a00cda", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Now let's check that it works" ] }, { "cell_type": "code", "execution_count": 42, "id": "977f99bd", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Your name is Bob.\n" ] } ], "source": [ "system_message=\"The user will tell you their name. When asked, repeat their name back to them.\"\n", "message_history = [\n", " {\"role\": \"user\", \"content\": \"My name is Bob.\"},\n", " {\"role\": \"assistant\", \"content\": \"Nice to meet you, Bob.\"}\n", "]\n", "message = \"What was my name again?\"\n", "print(get_ai_reply(message, system_message=system_message, message_history=message_history))" ] }, { "cell_type": "markdown", "id": "2ad45888", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Great! Now let's turn that into a test!" ] }, { "cell_type": "code", "execution_count": 43, "id": "83aa7546", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "system_message=\"The user will tell you their name. When asked, repeat their name back to them.\"\n", "message_history = [\n", " {\"role\": \"user\", \"content\": \"My name is Bob.\"},\n", " {\"role\": \"assistant\", \"content\": \"Nice to meet you, Bob.\"}\n", "]\n", "message = \"What was my name again?\"\n", "\n", "assert \"Bob\" in get_ai_reply(message, system_message=system_message, message_history=message_history)" ] }, { "cell_type": "markdown", "id": "25e498c8", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Alright here is our final function for integrating with OpenAI!" ] }, { "cell_type": "code", "execution_count": null, "id": "bfc5cd86", "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "import openai\n", "from dotenv import load_dotenv\n", "\n", "load_dotenv() # take environment variables from .env.\n", "\n", "# Define a function to get the AI's reply using the OpenAI API\n", "def get_ai_reply(message, model=\"gpt-3.5-turbo\", system_message=None, temperature=0, message_history=[]):\n", " # Initialize the messages list\n", " messages = []\n", " \n", " # Add the system message to the messages list\n", " if system_message is not None:\n", " messages += [{\"role\": \"system\", \"content\": system_message}]\n", "\n", " # Add the message history to the messages list\n", " if message_history is not None:\n", " messages += message_history\n", " \n", " # Add the user's message to the messages list\n", " messages += [{\"role\": \"user\", \"content\": message}]\n", " \n", " # Make an API call to the OpenAI ChatCompletion endpoint with the model and messages\n", " completion = openai.ChatCompletion.create(\n", " model=model,\n", " messages=messages,\n", " temperature=temperature\n", " )\n", " \n", " # Extract and return the AI's response from the API response\n", " return completion.choices[0].message.content.strip()\n", "\n", "# traditional unit tests\n", "assert isinstance(get_ai_reply(\"hello\"), str)\n", "assert isinstance(get_ai_reply(\"hello\", model=\"gpt-3.5-turbo\"), str)\n", "\n", "# non-deterministic unit tests\n", "assert \"world\" in get_ai_reply(\"hello\", model=\"gpt-3.5-turbo\", system_message=\"When I say 'hello' you simply reply with 'world.'\")\n", "\n", "system_message=\"The user will tell you their name. When asked, repeat their name back to them.\"\n", "message_history = [\n", " {\"role\": \"user\", \"content\": \"My name is Bob.\"},\n", " {\"role\": \"assistant\", \"content\": \"Nice to meet you, Bob.\"}\n", "]\n", "message = \"What was my name again?\"\n", "\n", "assert \"Bob\" in get_ai_reply(message, system_message=system_message, message_history=message_history)" ] }, { "cell_type": "code", "execution_count": 44, "id": "159eea8a", "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello! How can I assist you today?\n" ] } ], "source": [ "print(get_ai_reply(\"hello\"))" ] }, { "cell_type": "markdown", "id": "9de8f5da", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In the next few lessons, we will be building a graphical user interface around this functionality so we can have a real conversational experience." ] } ], "metadata": { "celltoolbar": "Slideshow", "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.10.8" } }, "nbformat": 4, "nbformat_minor": 5 }