diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d5df76cfe09ad64b333bced076900e485c2f84b2 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..c141b0e6ec252a8b1cc802905db09409a1789022 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +me/Arnav_Agrawal_Resume_2025.pdf filter=lfs diff=lfs merge=lfs -text diff --git a/1_lab1.ipynb b/1_lab1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..839c7fcd1faa9a1cac97d7876480de557c52a698 --- /dev/null +++ b/1_lab1.ipynb @@ -0,0 +1,1188 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Welcome to the start of your adventure in Agentic AI" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Are you ready for action??

\n", + " Have you completed all the setup steps in the setup folder?
\n", + " Have you checked out the guides in the guides folder?
\n", + " Well in that case, you're ready!!\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

This code is a live resource - keep an eye out for my updates

\n", + " I push updates regularly. As people ask questions or have problems, I add more examples and improve explanations. As a result, the code below might not be identical to the videos, as I've added more steps and better comments. Consider this like an interactive book that accompanies the lectures.

\n", + " I try to send emails regularly with important updates related to the course. You can find this in the 'Announcements' section of Udemy in the left sidebar. You can also choose to receive my emails via your Notification Settings in Udemy. I'm respectful of your inbox and always try to add value with my emails!\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### And please do remember to contact me if I can help\n", + "\n", + "And I love to connect: https://www.linkedin.com/in/eddonner/\n", + "\n", + "\n", + "### New to Notebooks like this one? Head over to the guides folder!\n", + "\n", + "Just to check you've already added the Python and Jupyter extensions to Cursor, if not already installed:\n", + "- Open extensions (View >> extensions)\n", + "- Search for python, and when the results show, click on the ms-python one, and Install it if not already installed\n", + "- Search for jupyter, and when the results show, click on the Microsoft one, and Install it if not already installed \n", + "Then View >> Explorer to bring back the File Explorer.\n", + "\n", + "And then:\n", + "1. Click where it says \"Select Kernel\" near the top right, and select the option called `.venv (Python 3.12.9)` or similar, which should be the first choice or the most prominent choice. You may need to choose \"Python Environments\" first.\n", + "2. Click in each \"cell\" below, starting with the cell immediately below this text, and press Shift+Enter to run\n", + "3. Enjoy!\n", + "\n", + "After you click \"Select Kernel\", if there is no option like `.venv (Python 3.12.9)` then please do the following: \n", + "1. On Mac: From the Cursor menu, choose Settings >> VS Code Settings (NOTE: be sure to select `VSCode Settings` not `Cursor Settings`); \n", + "On Windows PC: From the File menu, choose Preferences >> VS Code Settings(NOTE: be sure to select `VSCode Settings` not `Cursor Settings`) \n", + "2. In the Settings search bar, type \"venv\" \n", + "3. In the field \"Path to folder with a list of Virtual Environments\" put the path to the project root, like C:\\Users\\username\\projects\\agents (on a Windows PC) or /Users/username/projects/agents (on Mac or Linux). \n", + "And then try again.\n", + "\n", + "Having problems with missing Python versions in that list? Have you ever used Anaconda before? It might be interferring. Quit Cursor, bring up a new command line, and make sure that your Anaconda environment is deactivated: \n", + "`conda deactivate` \n", + "And if you still have any problems with conda and python versions, it's possible that you will need to run this too: \n", + "`conda config --set auto_activate_base false` \n", + "and then from within the Agents directory, you should be able to run `uv python list` and see the Python 3.12 version." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "# First let's do an import\n", + "from dotenv import load_dotenv\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Next it's time to load the API keys into environment variables\n", + "\n", + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OpenAI API Key exists and begins sk-proj-\n" + ] + } + ], + "source": [ + "# Check the keys\n", + "\n", + "import os\n", + "openai_api_key = os.getenv('OPENAI_API_KEY')\n", + "\n", + "if openai_api_key:\n", + " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n", + "else:\n", + " print(\"OpenAI API Key not set - please head to the troubleshooting guide in the setup folder\")\n", + " \n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "# And now - the all important import statement\n", + "# If you get an import error - head over to troubleshooting guide\n", + "\n", + "from openai import OpenAI" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "# And now we'll create an instance of the OpenAI class\n", + "# If you're not sure what it means to create an instance of a class - head over to the guides folder!\n", + "# If you get a NameError - head over to the guides folder to learn about NameErrors\n", + "\n", + "openai = OpenAI()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a list of messages in the familiar OpenAI format\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": \"What is 2+2?\"}]" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 + 2 equals 4.\n" + ] + } + ], + "source": [ + "# And now call it! Any problems, head to the troubleshooting guide\n", + "# This uses GPT 4.1 nano, the incredibly cheap model\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4.1-nano\",\n", + " messages=messages\n", + ")\n", + "\n", + "print(response.choices[0].message.content)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "# And now - let's ask for a question:\n", + "\n", + "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n", + "messages = [{\"role\": \"user\", \"content\": question}]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "If 3 cats can catch 3 mice in 3 minutes, how many cats are needed to catch 100 mice in 100 minutes?\n" + ] + } + ], + "source": [ + "# ask it - this uses GPT 4.1 mini, still cheap but more powerful than nano\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4.1-mini\",\n", + " messages=messages\n", + ")\n", + "\n", + "question = response.choices[0].message.content\n", + "\n", + "print(question)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ChatCompletionMessage(content='If 3 cats can catch 3 mice in 3 minutes, how many cats are needed to catch 100 mice in 100 minutes?', refusal=None, role='assistant', annotations=[], audio=None, function_call=None, tool_calls=None)" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response.choices[0].message" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "# form a new messages list\n", + "messages = [{\"role\": \"user\", \"content\": question}]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Let's analyze the problem step by step.\n", + "\n", + "### Given:\n", + "- 3 cats catch 3 mice in 3 minutes.\n", + "\n", + "### What do we need to find?\n", + "- Number of cats needed to catch 100 mice in 100 minutes.\n", + "\n", + "---\n", + "\n", + "### Step 1: Find the rate of catching mice per cat\n", + "\n", + "- 3 cats catch 3 mice in 3 minutes.\n", + "- So, total catching rate for 3 cats = 3 mice / 3 minutes = 1 mouse per minute.\n", + "- Therefore, 1 cat catches \\(\\frac{1 \\text{ mouse}}{3 \\text{ minutes}}\\).\n", + " \n", + "So, **each cat catches 1 mouse every 3 minutes**.\n", + "\n", + "---\n", + "\n", + "### Step 2: Find how many mice one cat can catch in 100 minutes\n", + "\n", + "- Since 1 cat catches 1 mouse in 3 minutes,\n", + "- In 100 minutes, 1 cat catches \\(\\frac{100}{3} \\approx 33.33\\) mice.\n", + "\n", + "---\n", + "\n", + "### Step 3: Find how many cats are needed to catch 100 mice in 100 minutes\n", + "\n", + "Let \\(x\\) be the number of cats needed.\n", + "\n", + "- Total mice caught by \\(x\\) cats in 100 minutes \\(= x \\times \\frac{100}{3}\\)\n", + "- This must equal 100 mice:\n", + " \n", + "\\[\n", + "x \\times \\frac{100}{3} = 100\n", + "\\]\n", + "\n", + "\\[\n", + "x = \\frac{100 \\times 3}{100} = 3\n", + "\\]\n", + "\n", + "---\n", + "\n", + "### **Answer:**\n", + "\n", + "\\[\n", + "\\boxed{3}\n", + "\\]\n", + "\n", + "So, **3 cats are needed to catch 100 mice in 100 minutes**.\n" + ] + } + ], + "source": [ + "# Ask it again\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4.1-mini\",\n", + " messages=messages\n", + ")\n", + "\n", + "answer = response.choices[0].message.content\n", + "print(answer)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "Let's analyze the problem step by step.\n", + "\n", + "### Given:\n", + "- 3 cats catch 3 mice in 3 minutes.\n", + "\n", + "### What do we need to find?\n", + "- Number of cats needed to catch 100 mice in 100 minutes.\n", + "\n", + "---\n", + "\n", + "### Step 1: Find the rate of catching mice per cat\n", + "\n", + "- 3 cats catch 3 mice in 3 minutes.\n", + "- So, total catching rate for 3 cats = 3 mice / 3 minutes = 1 mouse per minute.\n", + "- Therefore, 1 cat catches \\(\\frac{1 \\text{ mouse}}{3 \\text{ minutes}}\\).\n", + " \n", + "So, **each cat catches 1 mouse every 3 minutes**.\n", + "\n", + "---\n", + "\n", + "### Step 2: Find how many mice one cat can catch in 100 minutes\n", + "\n", + "- Since 1 cat catches 1 mouse in 3 minutes,\n", + "- In 100 minutes, 1 cat catches \\(\\frac{100}{3} \\approx 33.33\\) mice.\n", + "\n", + "---\n", + "\n", + "### Step 3: Find how many cats are needed to catch 100 mice in 100 minutes\n", + "\n", + "Let \\(x\\) be the number of cats needed.\n", + "\n", + "- Total mice caught by \\(x\\) cats in 100 minutes \\(= x \\times \\frac{100}{3}\\)\n", + "- This must equal 100 mice:\n", + " \n", + "\\[\n", + "x \\times \\frac{100}{3} = 100\n", + "\\]\n", + "\n", + "\\[\n", + "x = \\frac{100 \\times 3}{100} = 3\n", + "\\]\n", + "\n", + "---\n", + "\n", + "### **Answer:**\n", + "\n", + "\\[\n", + "\\boxed{3}\n", + "\\]\n", + "\n", + "So, **3 cats are needed to catch 100 mice in 100 minutes**." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Markdown, display\n", + "\n", + "display(Markdown(answer))\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Congratulations!\n", + "\n", + "That was a small, simple step in the direction of Agentic AI, with your new environment!\n", + "\n", + "Next time things get more interesting..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Exercise

\n", + " Now try this commercial application:
\n", + " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.
\n", + " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.
\n", + " Finally have 3 third LLM call propose the Agentic AI solution.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "One promising business area for an agentic AI opportunity is **personalized mental health and wellness coaching**.\n", + "\n", + "### Why this area?\n", + "\n", + "- **High demand:** Increasing awareness and focus on mental health create a growing market for accessible, personalized support.\n", + "- **Scalability:** AI can serve large numbers of users simultaneously, providing tailored interventions without requiring proportional human resource increases.\n", + "- **Agentic capabilities:** An agentic AI can actively monitor user inputs (e.g., mood tracking, journaling, behavior patterns), suggest personalized exercises or coping strategies, schedule reminders, and even proactively initiate supportive conversations or meditations.\n", + "- **Continuous learning:** The AI can learn from individual user responses over time to improve recommendations and adapt to changing needs.\n", + "- **Integration potential:** It can connect with wearables, calendars, and other health apps for a holistic approach to wellness.\n", + "\n", + "### Potential features\n", + "\n", + "- Real-time mood and stress detection from text, voice, or biometric data.\n", + "- Dynamic, context-aware coaching sessions.\n", + "- Goal setting and progress tracking with adaptive feedback.\n", + "- Crisis detection with escalation protocols.\n", + "- Privacy-focused data handling and compliance with healthcare regulations.\n", + "\n", + "### Business models to explore\n", + "\n", + "- Subscription-based access with tiered service levels.\n", + "- White-label solutions for healthcare providers or employers.\n", + "- Partnerships with insurance companies for preventive care incentives.\n", + "\n", + "Would you like me to help design an initial product concept or roadmap for this?\n" + ] + }, + { + "data": { + "text/plain": [ + "'Certainly! Let’s consider the **healthcare industry**, which is a domain ripe for agentic AI solutions due to its complexity and critical challenges.\\n\\n### Pain-point: Patient Data Management and Care Coordination\\n\\n**Challenge:** \\nHealthcare providers often struggle with managing vast amounts of patient data that is siloed across multiple systems (EHRs, labs, imaging centers, pharmacies). This fragmentation leads to delays in diagnosis, redundant testing, medication errors, and suboptimal care coordination among specialists, primary care physicians, and patients.\\n\\n**Why it’s ripe for an agentic AI solution:** \\nAn agentic AI could autonomously navigate diverse healthcare data systems, synthesize critical patient information in real-time, and proactively coordinate care tasks, such as scheduling appointments, flagging potential drug interactions, and suggesting personalized treatment plans by learning from the latest medical guidelines and patient history.\\n\\n---\\n\\nIf you’d like, I can help brainstorm what such an AI assistant could look like or how it might integrate with existing healthcare workflows.'" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# First create the messages:\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": \"Pick a business area that might be worth exploring for an agentic AI opportunity\"}]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4.1-mini\",\n", + " messages=messages\n", + ")\n", + "\n", + "# Then read the business idea:\n", + "\n", + "business_idea = response.choices[0].message.content\n", + "print(business_idea)\n", + "# And repeat!\n", + "messages1 = [{\"role\": \"user\", \"content\": \"Pick a pain-point in that industry - something challenging that might be ripe for an agentic AI solution\"}]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response1 = openai.chat.completions.create(\n", + " model=\"gpt-4.1-mini\",\n", + " messages=messages1\n", + ")\n", + "\n", + "# Then read the business idea:\n", + "\n", + "business_idea1 = response1.choices[0].message.content\n", + "business_idea1" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[?2026h\u001b[?25l\u001b[1Gpulling manifest ⠋ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[1Gpulling manifest ⠙ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[1Gpulling manifest ⠹ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[1Gpulling manifest ⠸ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[1Gpulling manifest ⠼ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[1Gpulling manifest ⠴ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[1Gpulling manifest ⠦ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[1Gpulling manifest ⠧ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 0% ▕ ▏ 2.2 MB/2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 0% ▕ ▏ 7.3 MB/2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 1% ▕ ▏ 20 MB/2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 1% ▕ ▏ 30 MB/2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 2% ▕ ▏ 33 MB/2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 2% ▕ ▏ 49 MB/2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 3% ▕ ▏ 52 MB/2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 3% ▕ ▏ 60 MB/2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 3% ▕ ▏ 67 MB/2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 4% ▕ ▏ 79 MB/2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 4% ▕ ▏ 87 MB/2.0 GB 87 MB/s 22s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 5% ▕ ▏ 99 MB/2.0 GB 87 MB/s 22s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 6% ▕█ ▏ 118 MB/2.0 GB 87 MB/s 21s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 7% ▕█ ▏ 133 MB/2.0 GB 87 MB/s 21s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 7% ▕█ ▏ 136 MB/2.0 GB 87 MB/s 21s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 7% ▕█ ▏ 150 MB/2.0 GB 87 MB/s 21s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 8% ▕█ ▏ 155 MB/2.0 GB 87 MB/s 21s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 8% ▕█ ▏ 168 MB/2.0 GB 87 MB/s 21s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 9% ▕█ ▏ 182 MB/2.0 GB 87 MB/s 21s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 9% ▕█ ▏ 187 MB/2.0 GB 87 MB/s 21s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 10% ▕█ ▏ 204 MB/2.0 GB 102 MB/s 17s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 11% ▕█ ▏ 224 MB/2.0 GB 102 MB/s 17s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 11% ▕██ ▏ 225 MB/2.0 GB 102 MB/s 17s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 12% ▕██ ▏ 243 MB/2.0 GB 102 MB/s 17s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 12% ▕██ ▏ 250 MB/2.0 GB 102 MB/s 17s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 13% ▕██ ▏ 263 MB/2.0 GB 102 MB/s 17s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 14% ▕██ ▏ 273 MB/2.0 GB 102 MB/s 17s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 14% ▕██ ▏ 285 MB/2.0 GB 102 MB/s 16s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 15% ▕██ ▏ 293 MB/2.0 GB 102 MB/s 16s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 15% ▕██ ▏ 304 MB/2.0 GB 102 MB/s 16s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 16% ▕██ ▏ 318 MB/2.0 GB 106 MB/s 16s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 16% ▕██ ▏ 322 MB/2.0 GB 106 MB/s 16s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 17% ▕██ ▏ 335 MB/2.0 GB 106 MB/s 15s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 17% ▕███ ▏ 353 MB/2.0 GB 106 MB/s 15s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 18% ▕███ ▏ 356 MB/2.0 GB 106 MB/s 15s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 18% ▕███ ▏ 363 MB/2.0 GB 106 MB/s 15s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 19% ▕███ ▏ 381 MB/2.0 GB 106 MB/s 15s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 19% ▕███ ▏ 384 MB/2.0 GB 106 MB/s 15s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 19% ▕███ ▏ 391 MB/2.0 GB 106 MB/s 15s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 21% ▕███ ▏ 414 MB/2.0 GB 106 MB/s 15s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 21% ▕███ ▏ 417 MB/2.0 GB 106 MB/s 15s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 21% ▕███ ▏ 431 MB/2.0 GB 106 MB/s 14s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 22% ▕███ ▏ 448 MB/2.0 GB 106 MB/s 14s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 23% ▕████ ▏ 455 MB/2.0 GB 106 MB/s 14s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 23% ▕████ ▏ 464 MB/2.0 GB 106 MB/s 14s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 24% ▕████ ▏ 479 MB/2.0 GB 106 MB/s 14s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 24% ▕████ ▏ 483 MB/2.0 GB 106 MB/s 14s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 24% ▕████ ▏ 493 MB/2.0 GB 106 MB/s 14s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 25% ▕████ ▏ 508 MB/2.0 GB 106 MB/s 14s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 25% ▕████ ▏ 513 MB/2.0 GB 106 MB/s 14s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 26% ▕████ ▏ 524 MB/2.0 GB 106 MB/s 13s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 27% ▕████ ▏ 540 MB/2.0 GB 107 MB/s 13s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 27% ▕████ ▏ 547 MB/2.0 GB 107 MB/s 13s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 28% ▕████ ▏ 559 MB/2.0 GB 107 MB/s 13s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 28% ▕█████ ▏ 572 MB/2.0 GB 107 MB/s 13s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 29% ▕█████ ▏ 582 MB/2.0 GB 107 MB/s 13s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 29% ▕█████ ▏ 592 MB/2.0 GB 107 MB/s 13s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 30% ▕█████ ▏ 603 MB/2.0 GB 107 MB/s 13s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 30% ▕█████ ▏ 607 MB/2.0 GB 107 MB/s 13s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 31% ▕█████ ▏ 617 MB/2.0 GB 107 MB/s 13s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 31% ▕█████ ▏ 634 MB/2.0 GB 107 MB/s 12s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 32% ▕█████ ▏ 643 MB/2.0 GB 107 MB/s 12s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 33% ▕█████ ▏ 657 MB/2.0 GB 107 MB/s 12s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 33% ▕█████ ▏ 663 MB/2.0 GB 107 MB/s 12s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 33% ▕██████ ▏ 675 MB/2.0 GB 107 MB/s 12s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 34% ▕██████ ▏ 688 MB/2.0 GB 107 MB/s 12s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 34% ▕██████ ▏ 695 MB/2.0 GB 107 MB/s 12s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 35% ▕██████ ▏ 702 MB/2.0 GB 107 MB/s 12s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 36% ▕██████ ▏ 718 MB/2.0 GB 107 MB/s 12s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 36% ▕██████ ▏ 723 MB/2.0 GB 107 MB/s 12s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 36% ▕██████ ▏ 725 MB/2.0 GB 107 MB/s 12s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 37% ▕██████ ▏ 748 MB/2.0 GB 106 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 37% ▕██████ ▏ 754 MB/2.0 GB 106 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 37% ▕██████ ▏ 756 MB/2.0 GB 106 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 38% ▕██████ ▏ 768 MB/2.0 GB 106 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 38% ▕██████ ▏ 774 MB/2.0 GB 106 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 38% ▕██████ ▏ 775 MB/2.0 GB 106 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 40% ▕███████ ▏ 803 MB/2.0 GB 106 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 40% ▕███████ ▏ 815 MB/2.0 GB 106 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 41% ▕███████ ▏ 818 MB/2.0 GB 106 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 41% ▕███████ ▏ 834 MB/2.0 GB 106 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 42% ▕███████ ▏ 846 MB/2.0 GB 105 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 42% ▕███████ ▏ 853 MB/2.0 GB 105 MB/s 11s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 43% ▕███████ ▏ 866 MB/2.0 GB 105 MB/s 10s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 44% ▕███████ ▏ 879 MB/2.0 GB 105 MB/s 10s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 44% ▕███████ ▏ 885 MB/2.0 GB 105 MB/s 10s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 44% ▕████████ ▏ 898 MB/2.0 GB 105 MB/s 10s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 45% ▕████████ ▏ 909 MB/2.0 GB 105 MB/s 10s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 45% ▕████████ ▏ 912 MB/2.0 GB 105 MB/s 10s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 46% ▕████████ ▏ 929 MB/2.0 GB 105 MB/s 10s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 46% ▕████████ ▏ 937 MB/2.0 GB 105 MB/s 10s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 47% ▕████████ ▏ 943 MB/2.0 GB 105 MB/s 10s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 47% ▕████████ ▏ 956 MB/2.0 GB 105 MB/s 10s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 48% ▕████████ ▏ 973 MB/2.0 GB 105 MB/s 9s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 49% ▕████████ ▏ 979 MB/2.0 GB 105 MB/s 9s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 49% ▕████████ ▏ 989 MB/2.0 GB 105 MB/s 9s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 49% ▕████████ ▏ 998 MB/2.0 GB 105 MB/s 9s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 50% ▕████████ ▏ 1.0 GB/2.0 GB 105 MB/s 9s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 50% ▕█████████ ▏ 1.0 GB/2.0 GB 105 MB/s 9s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 51% ▕█████████ ▏ 1.0 GB/2.0 GB 105 MB/s 9s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 51% ▕█████████ ▏ 1.0 GB/2.0 GB 105 MB/s 9s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 52% ▕█████████ ▏ 1.0 GB/2.0 GB 105 MB/s 9s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 52% ▕█████████ ▏ 1.1 GB/2.0 GB 106 MB/s 9s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 52% ▕█████████ ▏ 1.1 GB/2.0 GB 106 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 53% ▕█████████ ▏ 1.1 GB/2.0 GB 106 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 53% ▕█████████ ▏ 1.1 GB/2.0 GB 106 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 54% ▕█████████ ▏ 1.1 GB/2.0 GB 106 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 54% ▕█████████ ▏ 1.1 GB/2.0 GB 106 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 55% ▕█████████ ▏ 1.1 GB/2.0 GB 106 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 55% ▕█████████ ▏ 1.1 GB/2.0 GB 106 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 56% ▕██████████ ▏ 1.1 GB/2.0 GB 106 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 57% ▕██████████ ▏ 1.1 GB/2.0 GB 106 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 57% ▕██████████ ▏ 1.1 GB/2.0 GB 104 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 58% ▕██████████ ▏ 1.2 GB/2.0 GB 104 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 58% ▕██████████ ▏ 1.2 GB/2.0 GB 104 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 58% ▕██████████ ▏ 1.2 GB/2.0 GB 104 MB/s 8s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 59% ▕██████████ ▏ 1.2 GB/2.0 GB 104 MB/s 7s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 60% ▕██████████ ▏ 1.2 GB/2.0 GB 104 MB/s 7s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 60% ▕██████████ ▏ 1.2 GB/2.0 GB 104 MB/s 7s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 60% ▕██████████ ▏ 1.2 GB/2.0 GB 104 MB/s 7s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 61% ▕███████████ ▏ 1.2 GB/2.0 GB 104 MB/s 7s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 62% ▕███████████ ▏ 1.2 GB/2.0 GB 104 MB/s 7s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 62% ▕███████████ ▏ 1.3 GB/2.0 GB 103 MB/s 7s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 63% ▕███████████ ▏ 1.3 GB/2.0 GB 103 MB/s 7s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 63% ▕███████████ ▏ 1.3 GB/2.0 GB 103 MB/s 7s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 64% ▕███████████ ▏ 1.3 GB/2.0 GB 103 MB/s 7s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 64% ▕███████████ ▏ 1.3 GB/2.0 GB 103 MB/s 6s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 64% ▕███████████ ▏ 1.3 GB/2.0 GB 103 MB/s 6s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 65% ▕███████████ ▏ 1.3 GB/2.0 GB 103 MB/s 6s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 66% ▕███████████ ▏ 1.3 GB/2.0 GB 103 MB/s 6s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 66% ▕███████████ ▏ 1.3 GB/2.0 GB 103 MB/s 6s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 67% ▕███████████ ▏ 1.3 GB/2.0 GB 103 MB/s 6s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 67% ▕████████████ ▏ 1.4 GB/2.0 GB 103 MB/s 6s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 68% ▕████████████ ▏ 1.4 GB/2.0 GB 103 MB/s 6s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 68% ▕████████████ ▏ 1.4 GB/2.0 GB 103 MB/s 6s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 69% ▕████████████ ▏ 1.4 GB/2.0 GB 103 MB/s 6s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 69% ▕████████████ ▏ 1.4 GB/2.0 GB 103 MB/s 5s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 70% ▕████████████ ▏ 1.4 GB/2.0 GB 103 MB/s 5s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 71% ▕████████████ ▏ 1.4 GB/2.0 GB 103 MB/s 5s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 71% ▕████████████ ▏ 1.4 GB/2.0 GB 103 MB/s 5s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 71% ▕████████████ ▏ 1.4 GB/2.0 GB 103 MB/s 5s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 72% ▕████████████ ▏ 1.4 GB/2.0 GB 103 MB/s 5s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 72% ▕█████████████ ▏ 1.5 GB/2.0 GB 103 MB/s 5s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 73% ▕█████████████ ▏ 1.5 GB/2.0 GB 103 MB/s 5s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 74% ▕█████████████ ▏ 1.5 GB/2.0 GB 103 MB/s 5s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 74% ▕█████████████ ▏ 1.5 GB/2.0 GB 103 MB/s 5s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 75% ▕█████████████ ▏ 1.5 GB/2.0 GB 103 MB/s 4s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 75% ▕█████████████ ▏ 1.5 GB/2.0 GB 103 MB/s 4s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 75% ▕█████████████ ▏ 1.5 GB/2.0 GB 103 MB/s 4s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 76% ▕█████████████ ▏ 1.5 GB/2.0 GB 103 MB/s 4s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 77% ▕█████████████ ▏ 1.5 GB/2.0 GB 103 MB/s 4s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 77% ▕█████████████ ▏ 1.6 GB/2.0 GB 103 MB/s 4s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 78% ▕█████████████ ▏ 1.6 GB/2.0 GB 103 MB/s 4s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 78% ▕██████████████ ▏ 1.6 GB/2.0 GB 103 MB/s 4s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 79% ▕██████████████ ▏ 1.6 GB/2.0 GB 103 MB/s 4s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 79% ▕██████████████ ▏ 1.6 GB/2.0 GB 103 MB/s 4s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 80% ▕██████████████ ▏ 1.6 GB/2.0 GB 103 MB/s 3s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 80% ▕██████████████ ▏ 1.6 GB/2.0 GB 103 MB/s 3s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 81% ▕██████████████ ▏ 1.6 GB/2.0 GB 103 MB/s 3s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 82% ▕██████████████ ▏ 1.6 GB/2.0 GB 103 MB/s 3s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 82% ▕██████████████ ▏ 1.7 GB/2.0 GB 103 MB/s 3s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 83% ▕██████████████ ▏ 1.7 GB/2.0 GB 103 MB/s 3s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 83% ▕██████████████ ▏ 1.7 GB/2.0 GB 103 MB/s 3s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 83% ▕██████████████ ▏ 1.7 GB/2.0 GB 103 MB/s 3s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 84% ▕███████████████ ▏ 1.7 GB/2.0 GB 103 MB/s 3s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 85% ▕███████████████ ▏ 1.7 GB/2.0 GB 103 MB/s 2s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 85% ▕███████████████ ▏ 1.7 GB/2.0 GB 103 MB/s 2s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 86% ▕███████████████ ▏ 1.7 GB/2.0 GB 103 MB/s 2s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 86% ▕███████████████ ▏ 1.7 GB/2.0 GB 103 MB/s 2s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 87% ▕███████████████ ▏ 1.7 GB/2.0 GB 103 MB/s 2s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 87% ▕███████████████ ▏ 1.8 GB/2.0 GB 103 MB/s 2s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 88% ▕███████████████ ▏ 1.8 GB/2.0 GB 103 MB/s 2s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 88% ▕███████████████ ▏ 1.8 GB/2.0 GB 103 MB/s 2s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 89% ▕███████████████ ▏ 1.8 GB/2.0 GB 104 MB/s 2s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 89% ▕████████████████ ▏ 1.8 GB/2.0 GB 104 MB/s 2s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 90% ▕████████████████ ▏ 1.8 GB/2.0 GB 104 MB/s 2s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 90% ▕████████████████ ▏ 1.8 GB/2.0 GB 104 MB/s 1s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 91% ▕████████████████ ▏ 1.8 GB/2.0 GB 104 MB/s 1s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 91% ▕████████████████ ▏ 1.8 GB/2.0 GB 104 MB/s 1s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 91% ▕████████████████ ▏ 1.8 GB/2.0 GB 104 MB/s 1s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 92% ▕████████████████ ▏ 1.9 GB/2.0 GB 104 MB/s 1s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 93% ▕████████████████ ▏ 1.9 GB/2.0 GB 104 MB/s 1s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 93% ▕████████████████ ▏ 1.9 GB/2.0 GB 104 MB/s 1s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 94% ▕████████████████ ▏ 1.9 GB/2.0 GB 105 MB/s 1s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 94% ▕████████████████ ▏ 1.9 GB/2.0 GB 105 MB/s 1s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 95% ▕█████████████████ ▏ 1.9 GB/2.0 GB 105 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 95% ▕█████████████████ ▏ 1.9 GB/2.0 GB 105 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 96% ▕█████████████████ ▏ 1.9 GB/2.0 GB 105 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 96% ▕█████████████████ ▏ 1.9 GB/2.0 GB 105 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 97% ▕█████████████████ ▏ 2.0 GB/2.0 GB 105 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 97% ▕█████████████████ ▏ 2.0 GB/2.0 GB 105 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 98% ▕█████████████████ ▏ 2.0 GB/2.0 GB 105 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 98% ▕█████████████████ ▏ 2.0 GB/2.0 GB 105 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 99% ▕█████████████████ ▏ 2.0 GB/2.0 GB 105 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 99% ▕█████████████████ ▏ 2.0 GB/2.0 GB 104 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 99% ▕█████████████████ ▏ 2.0 GB/2.0 GB 104 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 99% ▕█████████████████ ▏ 2.0 GB/2.0 GB 104 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕█████████████████ ▏ 2.0 GB/2.0 GB 104 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕█████████████████ ▏ 2.0 GB/2.0 GB 104 MB/s 0s\u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest ⠋ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest ⠙ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest ⠹ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest ⠸ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest ⠼ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest ⠴ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest ⠦ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest ⠧ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest ⠇ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest ⠏ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest ⠋ \u001b[K\u001b[?25h\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[A\u001b[1Gpulling manifest \u001b[K\n", + "pulling dde5aa3fc5ff: 100% ▕██████████████████▏ 2.0 GB \u001b[K\n", + "pulling 966de95ca8a6: 100% ▕██████████████████▏ 1.4 KB \u001b[K\n", + "pulling fcc5a6bec9da: 100% ▕██████████████████▏ 7.7 KB \u001b[K\n", + "pulling a70ff7e570d9: 100% ▕██████████████████▏ 6.0 KB \u001b[K\n", + "pulling 56bb8bd477a5: 100% ▕██████████████████▏ 96 B \u001b[K\n", + "pulling 34bb5ab01051: 100% ▕██████████████████▏ 561 B \u001b[K\n", + "verifying sha256 digest \u001b[K\n", + "writing manifest \u001b[K\n", + "success \u001b[K\u001b[?25h\u001b[?2026l\n" + ] + } + ], + "source": [ + "!ollama pull llama3.2" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "One business area that could be worth exploring for an agile AI opportunity is:\n", + "\n", + "**Healthcare Management and Clinical Decision Support**\n", + "\n", + "The healthcare sector is increasingly digital, with the need to analyze large amounts of patient data, medical records, and various diagnostic outputs. An AI system that can analyze these data sets, provide insights, and make recommendations for clinical decision-making could be highly valuable.\n", + "\n", + "Here are some potential features and use cases:\n", + "\n", + "1. **Predictive Analytics**: Develop an AI model that can identify high-risk patients, predict patient outcomes, and provide personalized treatment plans.\n", + "2. **Clinical Decision Support Systems (CDSS)**: Create a platform that leverages expert systems and machine learning to guide clinicians in diagnosis, treatment, and medication management.\n", + "3. **Medical Imaging Analysis**: Design an AI system to analyze medical images (e.g., X-rays, CT scans) to detect abnormalities and aid in disease diagnosis.\n", + "4. **Natural Language Processing (NLP)**: Develop an NLP-based platform that can help clinicians interpret patient complaints, understand symptoms, and identify potential diagnoses.\n", + "\n", + "Benefits of implementing agile AI in this space:\n", + "\n", + "1. **Improved Patient Outcomes**: By providing more accurate diagnoses and personalized treatment plans, healthcare organizations can improve patient outcomes and reduce mortality rates.\n", + "2. **Enhanced Clinical Decision-Making**: Clinicians will have access to trusted, AI-driven recommendations that can aid in informed decision-making.\n", + "3. **Operational Efficiency**: Automated tasks, such as data analysis and transcription, can free up human clinicians to focus on more complex, high-value tasks.\n", + "4. **Cost Reduction**: By optimizing resource allocation and improving patient outcomes, healthcare organizations can reduce costs associated with medical care.\n", + "\n", + "To leverage agile AI in this space, it's essential to:\n", + "\n", + "1. **Collaborate with Clinicians and Subject Matter Experts**: Ensure that the AI system is grounded in clinical reality and aligns with established standards of practice.\n", + "2. **Conduct Rigorous Testing and Validation**: Verify the performance and accuracy of AI models through extensive testing and validation.\n", + "3. **Emphasize Explainability and Transparency**: Develop AI systems that provide clear explanations for their recommendations and insights to build trust among clinicians and stakeholders.\n", + "\n", + "By combining cutting-edge AI technologies with a deep understanding of clinical practices, we can create powerful tools that transform the healthcare landscape.\n" + ] + }, + { + "data": { + "text/plain": [ + "\"I'll choose a pain-point in the healthcare industry that I think could be ripe for an agentic AI solution:\\n\\n**Challenge:** Inefficient and time-consuming diagnosis of complex medical conditions, particularly those involving rare genetic disorders.\\n\\n**Background:** Diagnosing rare genetic disorders can be extremely challenging due to the complexity of the underlying biology, limited availability of patient data, and high variability in symptoms. Traditional approaches often rely on cumbersome clinical trials, multiple testing methods, and manual review of genomic data, leading to:\\n\\n1. Delays in diagnosis: Diagnosis times can range from months to years, which affects timely intervention and treatment.\\n2. High costs: Multiple tests, specialized equipment, and extensive genomic analysis contribute to high healthcare costs.\\n3. Limited patient outcomes: Many rare disorders have limited effective treatments, leading to poor patient outcomes.\\n\\n**Agentic AI Solution:** Development of an AI-powered diagnostic system that utilizes a combination of natural language processing (NLP), deep learning, and machine learning algorithms can help address these challenges. This solution involves:\\n\\n1. **Patient data integration**: Aggregating and analyzing genomic, clinical, and phenotypic data from multiple sources to identify patterns and relationships.\\n2. **Pattern discovery**: Using advanced NLP techniques to extract relevant information from patient records, including medical histories, genetic reports, and family pedigrees.\\n3. **Predictive modeling**: Developing machine learning models that predict the likelihood of a rare disease based on the identified patterns and patient characteristics.\\n4. **Continuous learning**: Training the AI system on new data and updating its knowledge graph to ensure it remains accurate and effective.\\n\\n**Benefits:**\\n\\n1. Improved diagnosis speed: Allowing for quicker identification of rare diseases, enabling timely treatment and interventions.\\n2. Enhanced accuracy: Minimizing errors through advanced pattern recognition and predictive modeling.\\n3. Personalized medicine: Providing tailored information about genetic predispositions and disease risk, empowering patients and families to make informed decisions.\\n\\n**Ethical considerations:** The development of such an AI system requires careful consideration of:\\n\\n1. Patient consent and data access: Ensuring patients provide informed consent for the collection and analysis of their data.\\n2. Data governance: Implementing robust standards and regulations for data handling, storage, and sharing.\\n3. Bias detection and mitigation: Regularly monitoring and addressing potential biases in AI decision-making.\\n\\n**Next steps:** Conducting pilot studies and collaborating with clinicians, patient advocacy groups, and relevant experts to validate the proposed solution and address any ethical concerns before scaling up for wider deployment.\"" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "openai = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n", + "# First create the messages:\n", + "MODEL = \"llama3.2\"\n", + "messages = [{\"role\": \"user\", \"content\": \"Pick a business area that might be worth exploring for an agentic AI opportunity\"}]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=MODEL,\n", + " messages=messages\n", + ")\n", + "\n", + "# Then read the business idea:\n", + "\n", + "business_idea = response.choices[0].message.content\n", + "print(business_idea)\n", + "# And repeat!\n", + "messages1 = [{\"role\": \"user\", \"content\": \"Pick a pain-point in that industry - something challenging that might be ripe for an agentic AI solution\"}]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response1 = openai.chat.completions.create(\n", + " model=MODEL,\n", + " messages=messages1\n", + ")\n", + "\n", + "# Then read the business idea:\n", + "\n", + "business_idea1 = response1.choices[0].message.content\n", + "business_idea1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "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.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/2_lab2.ipynb b/2_lab2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..f2fb1145fdeac42420048cc5c30470f42e0ce085 --- /dev/null +++ b/2_lab2.ipynb @@ -0,0 +1,914 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Welcome to the Second Lab - Week 1, Day 3\n", + "\n", + "Today we will work with lots of models! This is a way to get comfortable with APIs." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Important point - please read

\n", + " The way I collaborate with you may be different to other courses you've taken. I prefer not to type code while you watch. Rather, I execute Jupyter Labs, like this, and give you an intuition for what's going on. My suggestion is that you carefully execute this yourself, after watching the lecture. Add print statements to understand what's going on, and then come up with your own variations.

If you have time, I'd love it if you submit a PR for changes in the community_contributions folder - instructions in the resources. Also, if you have a Github account, use this to showcase your variations. Not only is this essential practice, but it demonstrates your skills to others, including perhaps future clients or employers...\n", + "
\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Start with imports - ask ChatGPT to explain any package that you don't know\n", + "\n", + "import os\n", + "import json\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "from anthropic import Anthropic\n", + "from IPython.display import Markdown, display" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Always remember to do this!\n", + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OpenAI API Key exists and begins sk-proj-\n", + "Anthropic API Key exists and begins xxxx\n", + "Google API Key exists and begins xx\n", + "DeepSeek API Key exists and begins xxx\n", + "Groq API Key not set (and this is optional)\n" + ] + } + ], + "source": [ + "# Print the key prefixes to help with any debugging\n", + "\n", + "openai_api_key = os.getenv('OPENAI_API_KEY')\n", + "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n", + "google_api_key = os.getenv('GOOGLE_API_KEY')\n", + "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n", + "groq_api_key = os.getenv('GROQ_API_KEY')\n", + "\n", + "if openai_api_key:\n", + " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n", + "else:\n", + " print(\"OpenAI API Key not set\")\n", + " \n", + "if anthropic_api_key:\n", + " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n", + "else:\n", + " print(\"Anthropic API Key not set (and this is optional)\")\n", + "\n", + "if google_api_key:\n", + " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n", + "else:\n", + " print(\"Google API Key not set (and this is optional)\")\n", + "\n", + "if deepseek_api_key:\n", + " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n", + "else:\n", + " print(\"DeepSeek API Key not set (and this is optional)\")\n", + "\n", + "if groq_api_key:\n", + " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n", + "else:\n", + " print(\"Groq API Key not set (and this is optional)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "request = \"Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their intelligence. \"\n", + "request += \"Answer only with the question, no explanation.\"\n", + "messages = [{\"role\": \"user\", \"content\": request}]" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'role': 'user',\n", + " 'content': 'Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their intelligence. Answer only with the question, no explanation.'}]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "messages" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "If you could design an ideal society with a unique governance structure that incorporates elements of democracy, meritocracy, and technocracy, what foundational principles would guide this society, and how would you address potential ethical dilemmas arising from each element?\n" + ] + } + ], + "source": [ + "openai = OpenAI()\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages,\n", + ")\n", + "question = response.choices[0].message.content\n", + "print(question)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "competitors = []\n", + "answers = []\n", + "messages = [{\"role\": \"user\", \"content\": question}]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "Designing an ideal society that melds elements of democracy, meritocracy, and technocracy involves establishing a governance structure that balances representation, expertise, and equitable opportunity. Below are foundational principles and strategies for addressing potential ethical dilemmas associated with each governance element.\n", + "\n", + "### Foundational Principles\n", + "\n", + "1. **Participatory Governance**: \n", + " - **Democracy**: Every citizen should have an opportunity to participate in decision-making through direct or indirect means, ensuring diverse representation. Regular referenda and citizen assemblies could be used to enhance direct participation in key issues.\n", + " - **Ethical Dilemma**: The challenge of uninformed decision-making by the general populace can lead to poor choices. To mitigate this, a well-designed education system emphasizing critical thinking and civic knowledge is essential.\n", + "\n", + "2. **Competence-based Leadership**:\n", + " - **Meritocracy**: Leaders and decision-makers should be selected based on demonstrated skills, experience, and achievements relevant to their roles. This would ensure that the most capable individuals are in charge of significant decisions, fostering efficiency.\n", + " - **Ethical Dilemma**: A strict meritocratic system may lead to elitism and may undervalue contribution from diverse backgrounds. To address this, merit evaluations can be made holistic, considering a range of intelligence forms, such as emotional and social intelligence, along with traditional academic and professional accomplishments.\n", + "\n", + "3. **Science and Technology Integration**:\n", + " - **Technocracy**: Technological and scientific expertise should guide policy-making, especially in areas of critical importance like health, environment, and infrastructure. Utilizing data-driven approaches and expert recommendations can enhance decision quality.\n", + " - **Ethical Dilemma**: A technocratic approach risks sidelining human values, customary practices, and ethical considerations in favor of efficiency or data. To handle this, a framework for ethical review must be established, where technology and science are applied not just for efficacy but are also subject to moral scrutiny.\n", + "\n", + "4. **Transparency and Accountability**:\n", + " - All actions and decisions within the governance structure must be transparent, with mechanisms for accountability. This can help combat corruption and enhance public trust in both leaders and the systematic approach to governance.\n", + " - **Ethical Dilemma**: Over-transparency may threaten privacy or decision-making freedom. A balance can be achieved by ensuring that while public interest is prioritized, individual rights and necessary confidentiality in certain matters are also respected.\n", + "\n", + "5. **Equity and Inclusion**:\n", + " - Ensure equitable access to opportunities, resources, and political representation for all citizens, no matter their background. This principle should permeate all aspects of society, ensuring that meritocratic systems are not dominated by systemic inequalities.\n", + " - **Ethical Dilemma**: Striving for equity can lead to tensions between opportunity and the pursuit of excellence. To reconcile this, mechanisms such as targeted mentorship programs and affirmative actions can provide support without undermining standards.\n", + "\n", + "6. **Adaptability and Evolution**:\n", + " - The governance structure should include mechanisms for continual self-assessment and adaptation based on complexity and changing circumstances. Debate, feedback loops, and pilot programs can facilitate this adaptability.\n", + " - **Ethical Dilemma**: Frequent changes can destabilize governance structures. To mitigate this, substantial changes should be subject to rigorous analysis and general consensus, ensuring thorough public discourse and understanding.\n", + "\n", + "### Ethical Oversight\n", + "\n", + "To navigate potential ethical dilemmas, an independent ethics committee can be established. This body would be tasked with reviewing decisions led by democratic, meritocratic, and technocratic elements and ensuring they align with the overarching values of the society. Public representation on this committee can also enhance transparency and accountability.\n", + "\n", + "In summary, the ideal society would be built on participatory governance, competence-based leadership, scientific integration, transparency, equity, and adaptability. These foundational principles would not only guide the society's operation but would serve as a bedrock for resolving the inherent ethical dilemmas of each governance element, fostering a balanced and just society." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# The API we know well\n", + "\n", + "model_name = \"gpt-4o-mini\"\n", + "\n", + "response = openai.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Anthropic has a slightly different API, and Max Tokens is required\n", + "\n", + "model_name = \"claude-3-7-sonnet-latest\"\n", + "\n", + "claude = Anthropic()\n", + "response = claude.messages.create(model=model_name, messages=messages, max_tokens=1000)\n", + "answer = response.content[0].text\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n", + "model_name = \"gemini-2.0-flash\"\n", + "\n", + "response = gemini.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n", + "model_name = \"deepseek-chat\"\n", + "\n", + "response = deepseek.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n", + "model_name = \"llama-3.3-70b-versatile\"\n", + "\n", + "response = groq.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## For the next cell, we will use Ollama\n", + "\n", + "Ollama runs a local web service that gives an OpenAI compatible endpoint, \n", + "and runs models locally using high performance C++ code.\n", + "\n", + "If you don't have Ollama, install it here by visiting https://ollama.com then pressing Download and following the instructions.\n", + "\n", + "After it's installed, you should be able to visit here: http://localhost:11434 and see the message \"Ollama is running\"\n", + "\n", + "You might need to restart Cursor (and maybe reboot). Then open a Terminal (control+\\`) and run `ollama serve`\n", + "\n", + "Useful Ollama commands (run these in the terminal, or with an exclamation mark in this notebook):\n", + "\n", + "`ollama pull ` downloads a model locally \n", + "`ollama ls` lists all the models you've downloaded \n", + "`ollama rm ` deletes the specified model from your downloads" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Super important - ignore me at your peril!

\n", + " The model called llama3.3 is FAR too large for home computers - it's not intended for personal computing and will consume all your resources! Stick with the nicely sized llama3.2 or llama3.2:1b and if you want larger, try llama3.1 or smaller variants of Qwen, Gemma, Phi or DeepSeek. See the the Ollama models page for a full list of models and sizes.\n", + " \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!ollama pull llama3.2" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "Designing an ideal society with a governance structure that combines the principles of democracy, meritocracy, and technocracy presents several challenges. However, by understanding the core values and implications of each governance type, we can create a framework for an innovative society that strives to balance individual freedoms, collective well-being, and technological advancements.\n", + "\n", + "Foundational Principles:\n", + "\n", + "1. **Participatory Democracy**: Inclusive participation is essential for creating a citizen-led government where the voices of citizens are actively engaged in decision-making processes.\n", + "2. **Meritocratic Hiring Process**: A fair, unbiased selection method would be implemented to recruit highly skilled technocrats and policymakers who prioritize the common good over personal interests.\n", + "3. **Technocratic Expertise**: Technicians with expertise in evidence-based policy analysis and development would work alongside elected officials, ensuring informed decision-making is prioritized.\n", + "4. **Personal Responsibility and Accountability**: Citizens would have a vested interest in taking personal responsibility for their actions, and policymakers would be held accountable for the outcomes of their decisions.\n", + "\n", + "Governance Structure:\n", + "\n", + "1. **Elected Legislative Assembly**: A fair, competitive election system would select representatives who embody democratic values. The number of seats allocated to each seat type and how votes are counted may vary depending on population.\n", + "2. **Technocratic Expert Advisory Panel**: Established by the Executive or Senate Board, this commission comprises impartial experts in public policy, technology, and economics.\n", + "3. **Governor-Representative Role**: Each constituency governor will have executive authority to act as a mediator and support their constituents directly.\n", + "\n", + "Addressing Ethical Dilemmas:\n", + "\n", + "1. Ensuring representation from underrepresented groups might be difficult.\n", + "2. Balancing individual freedoms with the collective good.\n", + "3. Making informed decisions about policies that impact society's sustainability.\n", + "4. Addressing privacy concerns related to surveillance technologies.\n", + "\n", + "Ethics Frameworks would be necessary.\n", + "\n", + "For instance we may use:\n", + "\n", + "1. **Utilitarianism**: This perspective maximizes overall happiness or well-being.\n", + "2. **Deontology**: Prioritize moral rules and duties, such as protecting human rights and life expectancy.\n", + "3. **Virtue Ethics**: This approach emphasizes the development of desirable character traits like empathy and justice in leaders.\n", + "\n", + "Implementation Challenges\n", + "\n", + "The following factors may influence the successful implementation of this vision:\n", + "\n", + "* Overcoming historical narratives tied to existing governance models: \n", + "* Rebuilding trust among citizens after being perceived as oppressive or ineffective.\n", + " Incentivizing participation and encouraging civic engagement\n", + "* Balancing individual freedoms with group identity needs\n", + "* Preventing corruption in the technocratic process\n", + "\n", + "In conclusion, designing an ideal society with a governance structure based on democracy, meritocracy, and technocracy will require ongoing evaluation and refinement. This would necessitate open communication among all stakeholders, collaborative decision-making processes, and innovative strategies to address existing challenges. The ethical frameworks used may play a substantial role in facilitating societal harmony." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n", + "model_name = \"llama3.2\"\n", + "\n", + "response = ollama.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['gpt-4o-mini', 'llama3.2']\n", + "[\"Designing an ideal society that melds elements of democracy, meritocracy, and technocracy involves establishing a governance structure that balances representation, expertise, and equitable opportunity. Below are foundational principles and strategies for addressing potential ethical dilemmas associated with each governance element.\\n\\n### Foundational Principles\\n\\n1. **Participatory Governance**: \\n - **Democracy**: Every citizen should have an opportunity to participate in decision-making through direct or indirect means, ensuring diverse representation. Regular referenda and citizen assemblies could be used to enhance direct participation in key issues.\\n - **Ethical Dilemma**: The challenge of uninformed decision-making by the general populace can lead to poor choices. To mitigate this, a well-designed education system emphasizing critical thinking and civic knowledge is essential.\\n\\n2. **Competence-based Leadership**:\\n - **Meritocracy**: Leaders and decision-makers should be selected based on demonstrated skills, experience, and achievements relevant to their roles. This would ensure that the most capable individuals are in charge of significant decisions, fostering efficiency.\\n - **Ethical Dilemma**: A strict meritocratic system may lead to elitism and may undervalue contribution from diverse backgrounds. To address this, merit evaluations can be made holistic, considering a range of intelligence forms, such as emotional and social intelligence, along with traditional academic and professional accomplishments.\\n\\n3. **Science and Technology Integration**:\\n - **Technocracy**: Technological and scientific expertise should guide policy-making, especially in areas of critical importance like health, environment, and infrastructure. Utilizing data-driven approaches and expert recommendations can enhance decision quality.\\n - **Ethical Dilemma**: A technocratic approach risks sidelining human values, customary practices, and ethical considerations in favor of efficiency or data. To handle this, a framework for ethical review must be established, where technology and science are applied not just for efficacy but are also subject to moral scrutiny.\\n\\n4. **Transparency and Accountability**:\\n - All actions and decisions within the governance structure must be transparent, with mechanisms for accountability. This can help combat corruption and enhance public trust in both leaders and the systematic approach to governance.\\n - **Ethical Dilemma**: Over-transparency may threaten privacy or decision-making freedom. A balance can be achieved by ensuring that while public interest is prioritized, individual rights and necessary confidentiality in certain matters are also respected.\\n\\n5. **Equity and Inclusion**:\\n - Ensure equitable access to opportunities, resources, and political representation for all citizens, no matter their background. This principle should permeate all aspects of society, ensuring that meritocratic systems are not dominated by systemic inequalities.\\n - **Ethical Dilemma**: Striving for equity can lead to tensions between opportunity and the pursuit of excellence. To reconcile this, mechanisms such as targeted mentorship programs and affirmative actions can provide support without undermining standards.\\n\\n6. **Adaptability and Evolution**:\\n - The governance structure should include mechanisms for continual self-assessment and adaptation based on complexity and changing circumstances. Debate, feedback loops, and pilot programs can facilitate this adaptability.\\n - **Ethical Dilemma**: Frequent changes can destabilize governance structures. To mitigate this, substantial changes should be subject to rigorous analysis and general consensus, ensuring thorough public discourse and understanding.\\n\\n### Ethical Oversight\\n\\nTo navigate potential ethical dilemmas, an independent ethics committee can be established. This body would be tasked with reviewing decisions led by democratic, meritocratic, and technocratic elements and ensuring they align with the overarching values of the society. Public representation on this committee can also enhance transparency and accountability.\\n\\nIn summary, the ideal society would be built on participatory governance, competence-based leadership, scientific integration, transparency, equity, and adaptability. These foundational principles would not only guide the society's operation but would serve as a bedrock for resolving the inherent ethical dilemmas of each governance element, fostering a balanced and just society.\", \"Designing an ideal society with a governance structure that combines the principles of democracy, meritocracy, and technocracy presents several challenges. However, by understanding the core values and implications of each governance type, we can create a framework for an innovative society that strives to balance individual freedoms, collective well-being, and technological advancements.\\n\\nFoundational Principles:\\n\\n1. **Participatory Democracy**: Inclusive participation is essential for creating a citizen-led government where the voices of citizens are actively engaged in decision-making processes.\\n2. **Meritocratic Hiring Process**: A fair, unbiased selection method would be implemented to recruit highly skilled technocrats and policymakers who prioritize the common good over personal interests.\\n3. **Technocratic Expertise**: Technicians with expertise in evidence-based policy analysis and development would work alongside elected officials, ensuring informed decision-making is prioritized.\\n4. **Personal Responsibility and Accountability**: Citizens would have a vested interest in taking personal responsibility for their actions, and policymakers would be held accountable for the outcomes of their decisions.\\n\\nGovernance Structure:\\n\\n1. **Elected Legislative Assembly**: A fair, competitive election system would select representatives who embody democratic values. The number of seats allocated to each seat type and how votes are counted may vary depending on population.\\n2. **Technocratic Expert Advisory Panel**: Established by the Executive or Senate Board, this commission comprises impartial experts in public policy, technology, and economics.\\n3. **Governor-Representative Role**: Each constituency governor will have executive authority to act as a mediator and support their constituents directly.\\n\\nAddressing Ethical Dilemmas:\\n\\n1. Ensuring representation from underrepresented groups might be difficult.\\n2. Balancing individual freedoms with the collective good.\\n3. Making informed decisions about policies that impact society's sustainability.\\n4. Addressing privacy concerns related to surveillance technologies.\\n\\nEthics Frameworks would be necessary.\\n\\nFor instance we may use:\\n\\n1. **Utilitarianism**: This perspective maximizes overall happiness or well-being.\\n2. **Deontology**: Prioritize moral rules and duties, such as protecting human rights and life expectancy.\\n3. **Virtue Ethics**: This approach emphasizes the development of desirable character traits like empathy and justice in leaders.\\n\\nImplementation Challenges\\n\\nThe following factors may influence the successful implementation of this vision:\\n\\n* Overcoming historical narratives tied to existing governance models: \\n* Rebuilding trust among citizens after being perceived as oppressive or ineffective.\\n Incentivizing participation and encouraging civic engagement\\n* Balancing individual freedoms with group identity needs\\n* Preventing corruption in the technocratic process\\n\\nIn conclusion, designing an ideal society with a governance structure based on democracy, meritocracy, and technocracy will require ongoing evaluation and refinement. This would necessitate open communication among all stakeholders, collaborative decision-making processes, and innovative strategies to address existing challenges. The ethical frameworks used may play a substantial role in facilitating societal harmony.\"]\n" + ] + } + ], + "source": [ + "# So where are we?\n", + "\n", + "print(competitors)\n", + "print(answers)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Competitor: gpt-4o-mini\n", + "\n", + "Designing an ideal society that melds elements of democracy, meritocracy, and technocracy involves establishing a governance structure that balances representation, expertise, and equitable opportunity. Below are foundational principles and strategies for addressing potential ethical dilemmas associated with each governance element.\n", + "\n", + "### Foundational Principles\n", + "\n", + "1. **Participatory Governance**: \n", + " - **Democracy**: Every citizen should have an opportunity to participate in decision-making through direct or indirect means, ensuring diverse representation. Regular referenda and citizen assemblies could be used to enhance direct participation in key issues.\n", + " - **Ethical Dilemma**: The challenge of uninformed decision-making by the general populace can lead to poor choices. To mitigate this, a well-designed education system emphasizing critical thinking and civic knowledge is essential.\n", + "\n", + "2. **Competence-based Leadership**:\n", + " - **Meritocracy**: Leaders and decision-makers should be selected based on demonstrated skills, experience, and achievements relevant to their roles. This would ensure that the most capable individuals are in charge of significant decisions, fostering efficiency.\n", + " - **Ethical Dilemma**: A strict meritocratic system may lead to elitism and may undervalue contribution from diverse backgrounds. To address this, merit evaluations can be made holistic, considering a range of intelligence forms, such as emotional and social intelligence, along with traditional academic and professional accomplishments.\n", + "\n", + "3. **Science and Technology Integration**:\n", + " - **Technocracy**: Technological and scientific expertise should guide policy-making, especially in areas of critical importance like health, environment, and infrastructure. Utilizing data-driven approaches and expert recommendations can enhance decision quality.\n", + " - **Ethical Dilemma**: A technocratic approach risks sidelining human values, customary practices, and ethical considerations in favor of efficiency or data. To handle this, a framework for ethical review must be established, where technology and science are applied not just for efficacy but are also subject to moral scrutiny.\n", + "\n", + "4. **Transparency and Accountability**:\n", + " - All actions and decisions within the governance structure must be transparent, with mechanisms for accountability. This can help combat corruption and enhance public trust in both leaders and the systematic approach to governance.\n", + " - **Ethical Dilemma**: Over-transparency may threaten privacy or decision-making freedom. A balance can be achieved by ensuring that while public interest is prioritized, individual rights and necessary confidentiality in certain matters are also respected.\n", + "\n", + "5. **Equity and Inclusion**:\n", + " - Ensure equitable access to opportunities, resources, and political representation for all citizens, no matter their background. This principle should permeate all aspects of society, ensuring that meritocratic systems are not dominated by systemic inequalities.\n", + " - **Ethical Dilemma**: Striving for equity can lead to tensions between opportunity and the pursuit of excellence. To reconcile this, mechanisms such as targeted mentorship programs and affirmative actions can provide support without undermining standards.\n", + "\n", + "6. **Adaptability and Evolution**:\n", + " - The governance structure should include mechanisms for continual self-assessment and adaptation based on complexity and changing circumstances. Debate, feedback loops, and pilot programs can facilitate this adaptability.\n", + " - **Ethical Dilemma**: Frequent changes can destabilize governance structures. To mitigate this, substantial changes should be subject to rigorous analysis and general consensus, ensuring thorough public discourse and understanding.\n", + "\n", + "### Ethical Oversight\n", + "\n", + "To navigate potential ethical dilemmas, an independent ethics committee can be established. This body would be tasked with reviewing decisions led by democratic, meritocratic, and technocratic elements and ensuring they align with the overarching values of the society. Public representation on this committee can also enhance transparency and accountability.\n", + "\n", + "In summary, the ideal society would be built on participatory governance, competence-based leadership, scientific integration, transparency, equity, and adaptability. These foundational principles would not only guide the society's operation but would serve as a bedrock for resolving the inherent ethical dilemmas of each governance element, fostering a balanced and just society.\n", + "Competitor: llama3.2\n", + "\n", + "Designing an ideal society with a governance structure that combines the principles of democracy, meritocracy, and technocracy presents several challenges. However, by understanding the core values and implications of each governance type, we can create a framework for an innovative society that strives to balance individual freedoms, collective well-being, and technological advancements.\n", + "\n", + "Foundational Principles:\n", + "\n", + "1. **Participatory Democracy**: Inclusive participation is essential for creating a citizen-led government where the voices of citizens are actively engaged in decision-making processes.\n", + "2. **Meritocratic Hiring Process**: A fair, unbiased selection method would be implemented to recruit highly skilled technocrats and policymakers who prioritize the common good over personal interests.\n", + "3. **Technocratic Expertise**: Technicians with expertise in evidence-based policy analysis and development would work alongside elected officials, ensuring informed decision-making is prioritized.\n", + "4. **Personal Responsibility and Accountability**: Citizens would have a vested interest in taking personal responsibility for their actions, and policymakers would be held accountable for the outcomes of their decisions.\n", + "\n", + "Governance Structure:\n", + "\n", + "1. **Elected Legislative Assembly**: A fair, competitive election system would select representatives who embody democratic values. The number of seats allocated to each seat type and how votes are counted may vary depending on population.\n", + "2. **Technocratic Expert Advisory Panel**: Established by the Executive or Senate Board, this commission comprises impartial experts in public policy, technology, and economics.\n", + "3. **Governor-Representative Role**: Each constituency governor will have executive authority to act as a mediator and support their constituents directly.\n", + "\n", + "Addressing Ethical Dilemmas:\n", + "\n", + "1. Ensuring representation from underrepresented groups might be difficult.\n", + "2. Balancing individual freedoms with the collective good.\n", + "3. Making informed decisions about policies that impact society's sustainability.\n", + "4. Addressing privacy concerns related to surveillance technologies.\n", + "\n", + "Ethics Frameworks would be necessary.\n", + "\n", + "For instance we may use:\n", + "\n", + "1. **Utilitarianism**: This perspective maximizes overall happiness or well-being.\n", + "2. **Deontology**: Prioritize moral rules and duties, such as protecting human rights and life expectancy.\n", + "3. **Virtue Ethics**: This approach emphasizes the development of desirable character traits like empathy and justice in leaders.\n", + "\n", + "Implementation Challenges\n", + "\n", + "The following factors may influence the successful implementation of this vision:\n", + "\n", + "* Overcoming historical narratives tied to existing governance models: \n", + "* Rebuilding trust among citizens after being perceived as oppressive or ineffective.\n", + " Incentivizing participation and encouraging civic engagement\n", + "* Balancing individual freedoms with group identity needs\n", + "* Preventing corruption in the technocratic process\n", + "\n", + "In conclusion, designing an ideal society with a governance structure based on democracy, meritocracy, and technocracy will require ongoing evaluation and refinement. This would necessitate open communication among all stakeholders, collaborative decision-making processes, and innovative strategies to address existing challenges. The ethical frameworks used may play a substantial role in facilitating societal harmony.\n" + ] + } + ], + "source": [ + "# It's nice to know how to use \"zip\"\n", + "for competitor, answer in zip(competitors, answers):\n", + " print(f\"Competitor: {competitor}\\n\\n{answer}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "# Let's bring this together - note the use of \"enumerate\"\n", + "\n", + "together = \"\"\n", + "for index, answer in enumerate(answers):\n", + " together += f\"# Response from competitor {index+1}\\n\\n\"\n", + " together += answer + \"\\n\\n\"" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# Response from competitor 1\n", + "\n", + "Designing an ideal society that melds elements of democracy, meritocracy, and technocracy involves establishing a governance structure that balances representation, expertise, and equitable opportunity. Below are foundational principles and strategies for addressing potential ethical dilemmas associated with each governance element.\n", + "\n", + "### Foundational Principles\n", + "\n", + "1. **Participatory Governance**: \n", + " - **Democracy**: Every citizen should have an opportunity to participate in decision-making through direct or indirect means, ensuring diverse representation. Regular referenda and citizen assemblies could be used to enhance direct participation in key issues.\n", + " - **Ethical Dilemma**: The challenge of uninformed decision-making by the general populace can lead to poor choices. To mitigate this, a well-designed education system emphasizing critical thinking and civic knowledge is essential.\n", + "\n", + "2. **Competence-based Leadership**:\n", + " - **Meritocracy**: Leaders and decision-makers should be selected based on demonstrated skills, experience, and achievements relevant to their roles. This would ensure that the most capable individuals are in charge of significant decisions, fostering efficiency.\n", + " - **Ethical Dilemma**: A strict meritocratic system may lead to elitism and may undervalue contribution from diverse backgrounds. To address this, merit evaluations can be made holistic, considering a range of intelligence forms, such as emotional and social intelligence, along with traditional academic and professional accomplishments.\n", + "\n", + "3. **Science and Technology Integration**:\n", + " - **Technocracy**: Technological and scientific expertise should guide policy-making, especially in areas of critical importance like health, environment, and infrastructure. Utilizing data-driven approaches and expert recommendations can enhance decision quality.\n", + " - **Ethical Dilemma**: A technocratic approach risks sidelining human values, customary practices, and ethical considerations in favor of efficiency or data. To handle this, a framework for ethical review must be established, where technology and science are applied not just for efficacy but are also subject to moral scrutiny.\n", + "\n", + "4. **Transparency and Accountability**:\n", + " - All actions and decisions within the governance structure must be transparent, with mechanisms for accountability. This can help combat corruption and enhance public trust in both leaders and the systematic approach to governance.\n", + " - **Ethical Dilemma**: Over-transparency may threaten privacy or decision-making freedom. A balance can be achieved by ensuring that while public interest is prioritized, individual rights and necessary confidentiality in certain matters are also respected.\n", + "\n", + "5. **Equity and Inclusion**:\n", + " - Ensure equitable access to opportunities, resources, and political representation for all citizens, no matter their background. This principle should permeate all aspects of society, ensuring that meritocratic systems are not dominated by systemic inequalities.\n", + " - **Ethical Dilemma**: Striving for equity can lead to tensions between opportunity and the pursuit of excellence. To reconcile this, mechanisms such as targeted mentorship programs and affirmative actions can provide support without undermining standards.\n", + "\n", + "6. **Adaptability and Evolution**:\n", + " - The governance structure should include mechanisms for continual self-assessment and adaptation based on complexity and changing circumstances. Debate, feedback loops, and pilot programs can facilitate this adaptability.\n", + " - **Ethical Dilemma**: Frequent changes can destabilize governance structures. To mitigate this, substantial changes should be subject to rigorous analysis and general consensus, ensuring thorough public discourse and understanding.\n", + "\n", + "### Ethical Oversight\n", + "\n", + "To navigate potential ethical dilemmas, an independent ethics committee can be established. This body would be tasked with reviewing decisions led by democratic, meritocratic, and technocratic elements and ensuring they align with the overarching values of the society. Public representation on this committee can also enhance transparency and accountability.\n", + "\n", + "In summary, the ideal society would be built on participatory governance, competence-based leadership, scientific integration, transparency, equity, and adaptability. These foundational principles would not only guide the society's operation but would serve as a bedrock for resolving the inherent ethical dilemmas of each governance element, fostering a balanced and just society.\n", + "\n", + "# Response from competitor 2\n", + "\n", + "Designing an ideal society with a governance structure that combines the principles of democracy, meritocracy, and technocracy presents several challenges. However, by understanding the core values and implications of each governance type, we can create a framework for an innovative society that strives to balance individual freedoms, collective well-being, and technological advancements.\n", + "\n", + "Foundational Principles:\n", + "\n", + "1. **Participatory Democracy**: Inclusive participation is essential for creating a citizen-led government where the voices of citizens are actively engaged in decision-making processes.\n", + "2. **Meritocratic Hiring Process**: A fair, unbiased selection method would be implemented to recruit highly skilled technocrats and policymakers who prioritize the common good over personal interests.\n", + "3. **Technocratic Expertise**: Technicians with expertise in evidence-based policy analysis and development would work alongside elected officials, ensuring informed decision-making is prioritized.\n", + "4. **Personal Responsibility and Accountability**: Citizens would have a vested interest in taking personal responsibility for their actions, and policymakers would be held accountable for the outcomes of their decisions.\n", + "\n", + "Governance Structure:\n", + "\n", + "1. **Elected Legislative Assembly**: A fair, competitive election system would select representatives who embody democratic values. The number of seats allocated to each seat type and how votes are counted may vary depending on population.\n", + "2. **Technocratic Expert Advisory Panel**: Established by the Executive or Senate Board, this commission comprises impartial experts in public policy, technology, and economics.\n", + "3. **Governor-Representative Role**: Each constituency governor will have executive authority to act as a mediator and support their constituents directly.\n", + "\n", + "Addressing Ethical Dilemmas:\n", + "\n", + "1. Ensuring representation from underrepresented groups might be difficult.\n", + "2. Balancing individual freedoms with the collective good.\n", + "3. Making informed decisions about policies that impact society's sustainability.\n", + "4. Addressing privacy concerns related to surveillance technologies.\n", + "\n", + "Ethics Frameworks would be necessary.\n", + "\n", + "For instance we may use:\n", + "\n", + "1. **Utilitarianism**: This perspective maximizes overall happiness or well-being.\n", + "2. **Deontology**: Prioritize moral rules and duties, such as protecting human rights and life expectancy.\n", + "3. **Virtue Ethics**: This approach emphasizes the development of desirable character traits like empathy and justice in leaders.\n", + "\n", + "Implementation Challenges\n", + "\n", + "The following factors may influence the successful implementation of this vision:\n", + "\n", + "* Overcoming historical narratives tied to existing governance models: \n", + "* Rebuilding trust among citizens after being perceived as oppressive or ineffective.\n", + " Incentivizing participation and encouraging civic engagement\n", + "* Balancing individual freedoms with group identity needs\n", + "* Preventing corruption in the technocratic process\n", + "\n", + "In conclusion, designing an ideal society with a governance structure based on democracy, meritocracy, and technocracy will require ongoing evaluation and refinement. This would necessitate open communication among all stakeholders, collaborative decision-making processes, and innovative strategies to address existing challenges. The ethical frameworks used may play a substantial role in facilitating societal harmony.\n", + "\n", + "\n" + ] + } + ], + "source": [ + "print(together)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "judge = f\"\"\"You are judging a competition between {len(competitors)} competitors.\n", + "Each model has been given this question:\n", + "\n", + "{question}\n", + "\n", + "Your job is to evaluate each response for clarity and strength of argument, and rank them in order of best to worst.\n", + "Respond with JSON, and only JSON, with the following format:\n", + "{{\"results\": [\"best competitor number\", \"second best competitor number\", ...]}}\n", + "\n", + "Here are the responses from each competitor:\n", + "\n", + "{together}\n", + "\n", + "Now respond with the JSON with the ranked order of the competitors, nothing else. Do not include markdown formatting or code blocks.\"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You are judging a competition between 2 competitors.\n", + "Each model has been given this question:\n", + "\n", + "If you could design an ideal society with a unique governance structure that incorporates elements of democracy, meritocracy, and technocracy, what foundational principles would guide this society, and how would you address potential ethical dilemmas arising from each element?\n", + "\n", + "Your job is to evaluate each response for clarity and strength of argument, and rank them in order of best to worst.\n", + "Respond with JSON, and only JSON, with the following format:\n", + "{\"results\": [\"best competitor number\", \"second best competitor number\", ...]}\n", + "\n", + "Here are the responses from each competitor:\n", + "\n", + "# Response from competitor 1\n", + "\n", + "Designing an ideal society that melds elements of democracy, meritocracy, and technocracy involves establishing a governance structure that balances representation, expertise, and equitable opportunity. Below are foundational principles and strategies for addressing potential ethical dilemmas associated with each governance element.\n", + "\n", + "### Foundational Principles\n", + "\n", + "1. **Participatory Governance**: \n", + " - **Democracy**: Every citizen should have an opportunity to participate in decision-making through direct or indirect means, ensuring diverse representation. Regular referenda and citizen assemblies could be used to enhance direct participation in key issues.\n", + " - **Ethical Dilemma**: The challenge of uninformed decision-making by the general populace can lead to poor choices. To mitigate this, a well-designed education system emphasizing critical thinking and civic knowledge is essential.\n", + "\n", + "2. **Competence-based Leadership**:\n", + " - **Meritocracy**: Leaders and decision-makers should be selected based on demonstrated skills, experience, and achievements relevant to their roles. This would ensure that the most capable individuals are in charge of significant decisions, fostering efficiency.\n", + " - **Ethical Dilemma**: A strict meritocratic system may lead to elitism and may undervalue contribution from diverse backgrounds. To address this, merit evaluations can be made holistic, considering a range of intelligence forms, such as emotional and social intelligence, along with traditional academic and professional accomplishments.\n", + "\n", + "3. **Science and Technology Integration**:\n", + " - **Technocracy**: Technological and scientific expertise should guide policy-making, especially in areas of critical importance like health, environment, and infrastructure. Utilizing data-driven approaches and expert recommendations can enhance decision quality.\n", + " - **Ethical Dilemma**: A technocratic approach risks sidelining human values, customary practices, and ethical considerations in favor of efficiency or data. To handle this, a framework for ethical review must be established, where technology and science are applied not just for efficacy but are also subject to moral scrutiny.\n", + "\n", + "4. **Transparency and Accountability**:\n", + " - All actions and decisions within the governance structure must be transparent, with mechanisms for accountability. This can help combat corruption and enhance public trust in both leaders and the systematic approach to governance.\n", + " - **Ethical Dilemma**: Over-transparency may threaten privacy or decision-making freedom. A balance can be achieved by ensuring that while public interest is prioritized, individual rights and necessary confidentiality in certain matters are also respected.\n", + "\n", + "5. **Equity and Inclusion**:\n", + " - Ensure equitable access to opportunities, resources, and political representation for all citizens, no matter their background. This principle should permeate all aspects of society, ensuring that meritocratic systems are not dominated by systemic inequalities.\n", + " - **Ethical Dilemma**: Striving for equity can lead to tensions between opportunity and the pursuit of excellence. To reconcile this, mechanisms such as targeted mentorship programs and affirmative actions can provide support without undermining standards.\n", + "\n", + "6. **Adaptability and Evolution**:\n", + " - The governance structure should include mechanisms for continual self-assessment and adaptation based on complexity and changing circumstances. Debate, feedback loops, and pilot programs can facilitate this adaptability.\n", + " - **Ethical Dilemma**: Frequent changes can destabilize governance structures. To mitigate this, substantial changes should be subject to rigorous analysis and general consensus, ensuring thorough public discourse and understanding.\n", + "\n", + "### Ethical Oversight\n", + "\n", + "To navigate potential ethical dilemmas, an independent ethics committee can be established. This body would be tasked with reviewing decisions led by democratic, meritocratic, and technocratic elements and ensuring they align with the overarching values of the society. Public representation on this committee can also enhance transparency and accountability.\n", + "\n", + "In summary, the ideal society would be built on participatory governance, competence-based leadership, scientific integration, transparency, equity, and adaptability. These foundational principles would not only guide the society's operation but would serve as a bedrock for resolving the inherent ethical dilemmas of each governance element, fostering a balanced and just society.\n", + "\n", + "# Response from competitor 2\n", + "\n", + "Designing an ideal society with a governance structure that combines the principles of democracy, meritocracy, and technocracy presents several challenges. However, by understanding the core values and implications of each governance type, we can create a framework for an innovative society that strives to balance individual freedoms, collective well-being, and technological advancements.\n", + "\n", + "Foundational Principles:\n", + "\n", + "1. **Participatory Democracy**: Inclusive participation is essential for creating a citizen-led government where the voices of citizens are actively engaged in decision-making processes.\n", + "2. **Meritocratic Hiring Process**: A fair, unbiased selection method would be implemented to recruit highly skilled technocrats and policymakers who prioritize the common good over personal interests.\n", + "3. **Technocratic Expertise**: Technicians with expertise in evidence-based policy analysis and development would work alongside elected officials, ensuring informed decision-making is prioritized.\n", + "4. **Personal Responsibility and Accountability**: Citizens would have a vested interest in taking personal responsibility for their actions, and policymakers would be held accountable for the outcomes of their decisions.\n", + "\n", + "Governance Structure:\n", + "\n", + "1. **Elected Legislative Assembly**: A fair, competitive election system would select representatives who embody democratic values. The number of seats allocated to each seat type and how votes are counted may vary depending on population.\n", + "2. **Technocratic Expert Advisory Panel**: Established by the Executive or Senate Board, this commission comprises impartial experts in public policy, technology, and economics.\n", + "3. **Governor-Representative Role**: Each constituency governor will have executive authority to act as a mediator and support their constituents directly.\n", + "\n", + "Addressing Ethical Dilemmas:\n", + "\n", + "1. Ensuring representation from underrepresented groups might be difficult.\n", + "2. Balancing individual freedoms with the collective good.\n", + "3. Making informed decisions about policies that impact society's sustainability.\n", + "4. Addressing privacy concerns related to surveillance technologies.\n", + "\n", + "Ethics Frameworks would be necessary.\n", + "\n", + "For instance we may use:\n", + "\n", + "1. **Utilitarianism**: This perspective maximizes overall happiness or well-being.\n", + "2. **Deontology**: Prioritize moral rules and duties, such as protecting human rights and life expectancy.\n", + "3. **Virtue Ethics**: This approach emphasizes the development of desirable character traits like empathy and justice in leaders.\n", + "\n", + "Implementation Challenges\n", + "\n", + "The following factors may influence the successful implementation of this vision:\n", + "\n", + "* Overcoming historical narratives tied to existing governance models: \n", + "* Rebuilding trust among citizens after being perceived as oppressive or ineffective.\n", + " Incentivizing participation and encouraging civic engagement\n", + "* Balancing individual freedoms with group identity needs\n", + "* Preventing corruption in the technocratic process\n", + "\n", + "In conclusion, designing an ideal society with a governance structure based on democracy, meritocracy, and technocracy will require ongoing evaluation and refinement. This would necessitate open communication among all stakeholders, collaborative decision-making processes, and innovative strategies to address existing challenges. The ethical frameworks used may play a substantial role in facilitating societal harmony.\n", + "\n", + "\n", + "\n", + "Now respond with the JSON with the ranked order of the competitors, nothing else. Do not include markdown formatting or code blocks.\n" + ] + } + ], + "source": [ + "print(judge)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "judge_messages = [{\"role\": \"user\", \"content\": judge}]" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\"results\": [\"1\", \"2\"]}\n" + ] + } + ], + "source": [ + "# Judgement time!\n", + "\n", + "ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n", + "model_name = \"llama3.2\"\n", + "\n", + "response = ollama.chat.completions.create(model=model_name, messages=judge_messages)\n", + "results = response.choices[0].message.content\n", + "print(results)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Rank 1: gpt-4o-mini\n", + "Rank 2: llama3.2\n" + ] + } + ], + "source": [ + "# OK let's turn this into results!\n", + "\n", + "results_dict = json.loads(results)\n", + "ranks = results_dict[\"results\"]\n", + "for index, result in enumerate(ranks):\n", + " competitor = competitors[int(result)-1]\n", + " print(f\"Rank {index+1}: {competitor}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Exercise

\n", + " Which pattern(s) did this use? Try updating this to add another Agentic design pattern.\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Commercial implications

\n", + " These kinds of patterns - to send a task to multiple models, and evaluate results,\n", + " are common where you need to improve the quality of your LLM response. This approach can be universally applied\n", + " to business projects where accuracy is critical.\n", + " \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.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/3_lab3.ipynb b/3_lab3.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..b43b8019e9168ecbeca6e029a6582fef3ea4ce7d --- /dev/null +++ b/3_lab3.ipynb @@ -0,0 +1,530 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Welcome to Lab 3 for Week 1 Day 4\n", + "\n", + "Today we're going to build something with immediate value!\n", + "\n", + "In the folder `me` I've put a single file `linkedin.pdf` - it's a PDF download of my LinkedIn profile.\n", + "\n", + "Please replace it with yours!\n", + "\n", + "I've also made a file called `summary.txt`\n", + "\n", + "We're not going to use Tools just yet - we're going to add the tool tomorrow." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Looking up packages

\n", + " In this lab, we're going to use the wonderful Gradio package for building quick UIs, \n", + " and we're also going to use the popular PyPDF2 PDF reader. You can get guides to these packages by asking \n", + " ChatGPT or Claude, and you find all open-source packages on the repository https://pypi.org.\n", + " \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [], + "source": [ + "# If you don't know what any of these packages do - you can always ask ChatGPT for a guide!\n", + "\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "from pypdf import PdfReader\n", + "import gradio as gr" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [], + "source": [ + "load_dotenv(override=True)\n", + "openai = OpenAI()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Ignoring wrong pointing object 6 0 (offset 0)\n", + "Ignoring wrong pointing object 8 0 (offset 0)\n", + "Ignoring wrong pointing object 10 0 (offset 0)\n", + "Ignoring wrong pointing object 13 0 (offset 0)\n", + "Ignoring wrong pointing object 22 0 (offset 0)\n", + "Ignoring wrong pointing object 23 0 (offset 0)\n" + ] + } + ], + "source": [ + "reader = PdfReader(\"me/Arnav_Agrawal_Resume_2025.pdf\")\n", + "linkedin = \"\"\n", + "for page in reader.pages:\n", + " text = page.extract_text()\n", + " if text:\n", + " linkedin += text" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arnav\tAgrawal\t\n", + "Jersey\tCity,\tNJ\t|\tEmail\t|\tLinkedIn\t|\t+1\t908-525-6248\t\n", + "OBJECTIVE\t\n", + "ProBicient\tanalytics\tprofessional\tfocused\ton\tdelivering\tbusiness\timpact\tthrough\tend-to-end\tdata\tinitiatives.\tSkilled\tin\t\n", + "building\tinnovative,\tinsight-driven\tsolutions\tthat\tachieve\tmeasurable\tresults.\t\n", + "EDUCATION\t \t\n", + "Stevens\tInstitute\tof\tTechnology,\tHoboken,\tNJ\t\t \t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tDecember\t2022\t\n", + "Master\tof\tScience,\tData\tScience\t \t\n", + "GPA:\t3.87\tRelated\tCourses:\tOptimization\tMethods,\tWeb\tMining,\tDeep\tLearning,\tApplied\tMachine\tLearning,\tStatistical\t\n", + "Methods,\tTime\tSeries\tAnalysis,\tPattern\tRecognition\t&\tClassiBication,\tGraph\tTheory\t\n", + "Manipal\tInstitute\tof\tTechnology,\tManipal,\tIndia\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tJ u l y \t2021\t\n", + "Bachelor\tof\tTechnology,\tElectronics\tand\tCommunication\tEngineering,\tMinor\tin\tComputational\tMathematics\t\n", + "CGPA:\t8.83\tRelated\tCourses:\tComputer\tVision,\tLinux\tand\tShell\tScripting,\tSQL,\tand\tDatabase\tManagement\t\n", + "SKILLS\t \t\n", + "• Language\t&\tTools:\tPython,\tSQL,\tC++,\tC#,\tR,\tSpark,\tTableau,\tMS\tOf@ice,\tHTML/CSS\t\n", + "• Frameworks\t&\tPlatforms:\tGit,\tHuggingFace\tTransformers,\tLangChain,\tCI/CD\tPipeline\t\n", + "• Techniques:\tGenerative\tAI\t(LLMs,\tPrompt\tEngineering,\tAgentic\tAI),\tNLP ,\tDeep\tLearning,\tMachine\tLearning,\tData\t\n", + "Analytics,\tFeature\tEngineering,\tModel\tEvaluation\t\n", + "• CertiQications:\tNVIDIA\tCerti@ied\tAssociate:\tGenerative\tAI\tLLMs,\tIBM\tData\tScience\tSpecialization\t\n", + "EXPERIENCE\t \t\n", + "EXL\tService\tAnalytics,\tNew\tYork\tCity,\tUSA\t(Banking\t&\tFinancial\tAnalytics)\t\t \t\t\t\t\t\t\t\t\t\t\t \t\t\t\t\t\t\t\t\t\t\t\t\t\tMay\t2022\t–\tPresent\t\n", + "Data\tScientist\t(Consultant\tto\tLeading\tU.S.\tBank)\t\n", + "Suspicious\tActivity\tReport\t(SAR)\tReview\tProcess\tAutomation\t\n", + "• Streamlined\tcase\treviews\tby\tautomating\tworkBlows,\treducing\tmanual\teffort\tand\taccelerating\tturnaround\ttime\t\n", + "• Implemented\ta\t.NET\tCore\tUI\tand\tbackend\tsystem\tto\tautomate\tdata\taggregation\tvia\tSQL\tand\tAPIs,\tdelivered\t\n", + "using\tAgile\tpractices\tand\tCI/CD\tpipelines\t\n", + "• Elevated\tworkBlow\tefBiciency\tand\tlaid\tthe\tfoundation\tfor\tGenAI\tintegration\tto\tauto-generate\tCase\tNarratives\t\n", + "and\tDigests,\tenabling\tfaster,\tintelligence-driven\tdecision-making\t\n", + "• Spearheaded\ta\t7-member\tteam,\toverseeing\tdelivery,\tstakeholder\talignment,\tand\tongoing\tfeature\tplanning\t\n", + "Conversational\tBI\t\n", + "• Conceptualized\ta\tnatural\tlanguage\tinterface\tto\tdemocratize\tcredit\tcard\tdata\tinsights\tfor\tbusiness\tusers\t\n", + "• Designed\tan\tNLP-powered\tchatbot\twith\ttext-to-SQL\tcapabilities\tfor\treal-time\tdata\tquerying\tand\texploration\t\n", + "• Delivered\tdynamic\tinsights,\tcharts,\ttables,\tand\tSQL\toutputs\tvia\tconversational\tinputs,\tboosting\tself-serve\t\n", + "analytics\tadoption\t\n", + "• Drove\tkey\tsolution\tphases—from\tarchitecture\tand\tprototyping\tto\tdeployment\tand\tstakeholder\tdemonstrations\t\n", + "Risk\tDecisioning-as-a-Service\t(RDaaS)\t\n", + "• Architected\ta\tdigital\tlending\tsolution\tto\thelp\tbanks\toptimize\tapproval\trates\twhile\tmaintaining\trisk\tthresholds\t\n", + "• Orchestrated\ta\tscalable\tML\tpipeline\tfor\tdata\tingestion,\tmodeling,\tand\texplainability\ton\tcloud\tinfrastructure\t\n", + "• Accelerated\ttime-to-decision\tby\t4–6x\tand\tenabled\tup\tto\t40%\tincrease\tin\tvolume\twith\tno\tadded\tcredit\texposure\t\n", + "• Presented\toutcomes\tand\tplatform\tcapabilities\tto\tsenior\tleadership\tto\tdrive\tadoption\tand\tstrategic\talignment\t\n", + "Virtuous\tTransactional\tAnalytics,\tNoida,\tIndia\t(Healthcare\tAnalytics)\t\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tJanuary\t2021\t–\tJuly\t2021\t\n", + "Data\tScience\tIntern\t-\tAutomation\tof\tcase-intake\tfor\tclassifying\tDrug\tSafety\tInformation\t(Pharmacovigilance)\t\n", + "• Administered\tNLP\ttechniques\t(NER,\tcoreference,\trelation\textraction)\tto\tbiomedical\tliterature,\tachieving\t90%\t\n", + "accuracy\tin\tdrug\tsafety\tcase\tintake\t\n", + "• Deployed\ta\t99%-accurate\tspam\tBilter\tand\tUI\tto\tautomate\tgeneration\tof\tIndividual\tCase\tSafety\tReports\t\n", + "Solytics\tPartners,\tPune,\tIndia\t(Banking\t&\tFinancial\tAnalytics)\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tMay\t2020\t–\tAugust\t2020\t\n", + "Data\tScience\tIntern\t-\tAnti-Money\tLaundering\t(AML)\t\n", + "• Developed\tML\tmodels\tfor\tfraud\tdetection\tand\ta\tcredit\trisk\tscorecard\tusing\tWOE/IV\tfor\taccurate\tcredit\tscoring\t\n", + "• Created\ta\tweb-based\tAutoML\tinterface\tthat\tenabled\tmodel\tconBiguration\tand\toutput\tvisualization\t\n", + "PROJECTS\t\n", + "Post-OCR\tCorrection\t\t \t \t \t\t\t\t\t\t\t \t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tJanuary\t2022\t–\tMay\t2022\t\n", + "• Implemented\ta\tBERT-based\tcontext-aware\terror\tcorrection\tsystem\tusing\tHuggingFace\tNeuspell;\timproved\tOCR\t\n", + "output\taccuracy\tto\t97%\t\n", + "Ted\tTalk\tRecommender\tSystem\t\t \t \t \t \t\t\t\t\t\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSeptember\t2021\t–\tDecember\t2021\t\n", + "• Built\ta\tcontent-based\trecommender\tusing\ttranscript\tsentiment\tanalysis\tand\ttopic\tmodeling;\tachieved\t83%\t\n", + "recommendation\taccuracy\t\n", + "Smart\tFarming\tusing\tConvolutional\tNeural\tNetworks\t\t\t \t \t \t\t\t\t\t\t\t\t\t\t\t\t\t\tMarch\t2019\t–\tOctober\t2019\t\n", + "• Engineered\tan\tArduino-powered\tdevice\twith\tCNNs\tfor\treal-time\tcrop\thealth\tmonitoring;\treached\t96%\taccuracy\t\n", + "using\tlow-cost\tsensors\n" + ] + } + ], + "source": [ + "print(linkedin)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n", + " summary = f.read()" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "name = \"Arnav Agrawal\"" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n", + "particularly questions related to {name}'s career, background, skills and experience. \\\n", + "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n", + "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n", + "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n", + "If you don't know the answer, say so.\"\n", + "\n", + "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n", + "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"You are acting as Arnav Agrawal. You are answering questions on Arnav Agrawal's website, particularly questions related to Arnav Agrawal's career, background, skills and experience. Your responsibility is to represent Arnav Agrawal for interactions on the website as faithfully as possible. You are given a summary of Arnav Agrawal's background and LinkedIn profile which you can use to answer questions. Be professional and engaging, as if talking to a potential client or future employer who came across the website. If you don't know the answer, say so.\\n\\n## Summary:\\nMy name is Arnav Agrawal. I'm an data scientist, business analyst. I'm originally from India, but I moved to NYC in 2021.\\nA highly motivated, diligent, focused and adaptable individual. A Data Science Enthusiast with experience in Data Analytics, Machine Learning, Natural Language Processing and Web Development. I have worked in Financial and Healthcare sector. I have graduated from Stevens Institute of Technology by completing Master’s in Data Science. I would like to connect with different people to know moreabout the industry world and how to make a career in it.\\n\\n## LinkedIn Profile:\\nArnav\\tAgrawal\\t\\nJersey\\tCity,\\tNJ\\t|\\tEmail\\t|\\tLinkedIn\\t|\\t+1\\t908-525-6248\\t\\nOBJECTIVE\\t\\nProBicient\\tanalytics\\tprofessional\\tfocused\\ton\\tdelivering\\tbusiness\\timpact\\tthrough\\tend-to-end\\tdata\\tinitiatives.\\tSkilled\\tin\\t\\nbuilding\\tinnovative,\\tinsight-driven\\tsolutions\\tthat\\tachieve\\tmeasurable\\tresults.\\t\\nEDUCATION\\t \\t\\nStevens\\tInstitute\\tof\\tTechnology,\\tHoboken,\\tNJ\\t\\t \\t \\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t \\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tDecember\\t2022\\t\\nMaster\\tof\\tScience,\\tData\\tScience\\t \\t\\nGPA:\\t3.87\\tRelated\\tCourses:\\tOptimization\\tMethods,\\tWeb\\tMining,\\tDeep\\tLearning,\\tApplied\\tMachine\\tLearning,\\tStatistical\\t\\nMethods,\\tTime\\tSeries\\tAnalysis,\\tPattern\\tRecognition\\t&\\tClassiBication,\\tGraph\\tTheory\\t\\nManipal\\tInstitute\\tof\\tTechnology,\\tManipal,\\tIndia\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tJ u l y \\t2021\\t\\nBachelor\\tof\\tTechnology,\\tElectronics\\tand\\tCommunication\\tEngineering,\\tMinor\\tin\\tComputational\\tMathematics\\t\\nCGPA:\\t8.83\\tRelated\\tCourses:\\tComputer\\tVision,\\tLinux\\tand\\tShell\\tScripting,\\tSQL,\\tand\\tDatabase\\tManagement\\t\\nSKILLS\\t \\t\\n• Language\\t&\\tTools:\\tPython,\\tSQL,\\tC++,\\tC#,\\tR,\\tSpark,\\tTableau,\\tMS\\tOf@ice,\\tHTML/CSS\\t\\n• Frameworks\\t&\\tPlatforms:\\tGit,\\tHuggingFace\\tTransformers,\\tLangChain,\\tCI/CD\\tPipeline\\t\\n• Techniques:\\tGenerative\\tAI\\t(LLMs,\\tPrompt\\tEngineering,\\tAgentic\\tAI),\\tNLP ,\\tDeep\\tLearning,\\tMachine\\tLearning,\\tData\\t\\nAnalytics,\\tFeature\\tEngineering,\\tModel\\tEvaluation\\t\\n• CertiQications:\\tNVIDIA\\tCerti@ied\\tAssociate:\\tGenerative\\tAI\\tLLMs,\\tIBM\\tData\\tScience\\tSpecialization\\t\\nEXPERIENCE\\t \\t\\nEXL\\tService\\tAnalytics,\\tNew\\tYork\\tCity,\\tUSA\\t(Banking\\t&\\tFinancial\\tAnalytics)\\t\\t \\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t \\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tMay\\t2022\\t–\\tPresent\\t\\nData\\tScientist\\t(Consultant\\tto\\tLeading\\tU.S.\\tBank)\\t\\nSuspicious\\tActivity\\tReport\\t(SAR)\\tReview\\tProcess\\tAutomation\\t\\n• Streamlined\\tcase\\treviews\\tby\\tautomating\\tworkBlows,\\treducing\\tmanual\\teffort\\tand\\taccelerating\\tturnaround\\ttime\\t\\n• Implemented\\ta\\t.NET\\tCore\\tUI\\tand\\tbackend\\tsystem\\tto\\tautomate\\tdata\\taggregation\\tvia\\tSQL\\tand\\tAPIs,\\tdelivered\\t\\nusing\\tAgile\\tpractices\\tand\\tCI/CD\\tpipelines\\t\\n• Elevated\\tworkBlow\\tefBiciency\\tand\\tlaid\\tthe\\tfoundation\\tfor\\tGenAI\\tintegration\\tto\\tauto-generate\\tCase\\tNarratives\\t\\nand\\tDigests,\\tenabling\\tfaster,\\tintelligence-driven\\tdecision-making\\t\\n• Spearheaded\\ta\\t7-member\\tteam,\\toverseeing\\tdelivery,\\tstakeholder\\talignment,\\tand\\tongoing\\tfeature\\tplanning\\t\\nConversational\\tBI\\t\\n• Conceptualized\\ta\\tnatural\\tlanguage\\tinterface\\tto\\tdemocratize\\tcredit\\tcard\\tdata\\tinsights\\tfor\\tbusiness\\tusers\\t\\n• Designed\\tan\\tNLP-powered\\tchatbot\\twith\\ttext-to-SQL\\tcapabilities\\tfor\\treal-time\\tdata\\tquerying\\tand\\texploration\\t\\n• Delivered\\tdynamic\\tinsights,\\tcharts,\\ttables,\\tand\\tSQL\\toutputs\\tvia\\tconversational\\tinputs,\\tboosting\\tself-serve\\t\\nanalytics\\tadoption\\t\\n• Drove\\tkey\\tsolution\\tphases—from\\tarchitecture\\tand\\tprototyping\\tto\\tdeployment\\tand\\tstakeholder\\tdemonstrations\\t\\nRisk\\tDecisioning-as-a-Service\\t(RDaaS)\\t\\n• Architected\\ta\\tdigital\\tlending\\tsolution\\tto\\thelp\\tbanks\\toptimize\\tapproval\\trates\\twhile\\tmaintaining\\trisk\\tthresholds\\t\\n• Orchestrated\\ta\\tscalable\\tML\\tpipeline\\tfor\\tdata\\tingestion,\\tmodeling,\\tand\\texplainability\\ton\\tcloud\\tinfrastructure\\t\\n• Accelerated\\ttime-to-decision\\tby\\t4–6x\\tand\\tenabled\\tup\\tto\\t40%\\tincrease\\tin\\tvolume\\twith\\tno\\tadded\\tcredit\\texposure\\t\\n• Presented\\toutcomes\\tand\\tplatform\\tcapabilities\\tto\\tsenior\\tleadership\\tto\\tdrive\\tadoption\\tand\\tstrategic\\talignment\\t\\nVirtuous\\tTransactional\\tAnalytics,\\tNoida,\\tIndia\\t(Healthcare\\tAnalytics)\\t\\t \\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tJanuary\\t2021\\t–\\tJuly\\t2021\\t\\nData\\tScience\\tIntern\\t-\\tAutomation\\tof\\tcase-intake\\tfor\\tclassifying\\tDrug\\tSafety\\tInformation\\t(Pharmacovigilance)\\t\\n• Administered\\tNLP\\ttechniques\\t(NER,\\tcoreference,\\trelation\\textraction)\\tto\\tbiomedical\\tliterature,\\tachieving\\t90%\\t\\naccuracy\\tin\\tdrug\\tsafety\\tcase\\tintake\\t\\n• Deployed\\ta\\t99%-accurate\\tspam\\tBilter\\tand\\tUI\\tto\\tautomate\\tgeneration\\tof\\tIndividual\\tCase\\tSafety\\tReports\\t\\nSolytics\\tPartners,\\tPune,\\tIndia\\t(Banking\\t&\\tFinancial\\tAnalytics)\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tMay\\t2020\\t–\\tAugust\\t2020\\t\\nData\\tScience\\tIntern\\t-\\tAnti-Money\\tLaundering\\t(AML)\\t\\n• Developed\\tML\\tmodels\\tfor\\tfraud\\tdetection\\tand\\ta\\tcredit\\trisk\\tscorecard\\tusing\\tWOE/IV\\tfor\\taccurate\\tcredit\\tscoring\\t\\n• Created\\ta\\tweb-based\\tAutoML\\tinterface\\tthat\\tenabled\\tmodel\\tconBiguration\\tand\\toutput\\tvisualization\\t\\nPROJECTS\\t\\nPost-OCR\\tCorrection\\t\\t \\t \\t \\t\\t\\t\\t\\t\\t\\t \\t \\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tJanuary\\t2022\\t–\\tMay\\t2022\\t\\n• Implemented\\ta\\tBERT-based\\tcontext-aware\\terror\\tcorrection\\tsystem\\tusing\\tHuggingFace\\tNeuspell;\\timproved\\tOCR\\t\\noutput\\taccuracy\\tto\\t97%\\t\\nTed\\tTalk\\tRecommender\\tSystem\\t\\t \\t \\t \\t \\t\\t\\t\\t\\t\\t \\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tSeptember\\t2021\\t–\\tDecember\\t2021\\t\\n• Built\\ta\\tcontent-based\\trecommender\\tusing\\ttranscript\\tsentiment\\tanalysis\\tand\\ttopic\\tmodeling;\\tachieved\\t83%\\t\\nrecommendation\\taccuracy\\t\\nSmart\\tFarming\\tusing\\tConvolutional\\tNeural\\tNetworks\\t\\t\\t \\t \\t \\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tMarch\\t2019\\t–\\tOctober\\t2019\\t\\n• Engineered\\tan\\tArduino-powered\\tdevice\\twith\\tCNNs\\tfor\\treal-time\\tcrop\\thealth\\tmonitoring;\\treached\\t96%\\taccuracy\\t\\nusing\\tlow-cost\\tsensors\\n\\nWith this context, please chat with the user, always staying in character as Arnav Agrawal.\"" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "system_prompt" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "def chat(message, history):\n", + " messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n", + " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n", + " return response.choices[0].message.content" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "* Running on local URL: http://127.0.0.1:7862\n", + "* To create a public link, set `share=True` in `launch()`.\n" + ] + }, + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gr.ChatInterface(chat, type=\"messages\").launch()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A lot is about to happen...\n", + "\n", + "1. Be able to ask an LLM to evaluate an answer\n", + "2. Be able to rerun if the answer fails evaluation\n", + "3. Put this together into 1 workflow\n", + "\n", + "All without any Agentic framework!" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a Pydantic model for the Evaluation\n", + "\n", + "from pydantic import BaseModel\n", + "\n", + "class Evaluation(BaseModel):\n", + " is_acceptable: bool\n", + " feedback: str\n" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [], + "source": [ + "evaluator_system_prompt = f\"You are an evaluator that decides whether a response to a question is acceptable. \\\n", + "You are provided with a conversation between a User and an Agent. Your task is to decide whether the Agent's latest response is acceptable quality. \\\n", + "The Agent is playing the role of {name} and is representing {name} on their website. \\\n", + "The Agent has been instructed to be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n", + "The Agent has been provided with context on {name} in the form of their summary and LinkedIn details. Here's the information:\"\n", + "\n", + "evaluator_system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n", + "evaluator_system_prompt += f\"With this context, please evaluate the latest response, replying with whether the response is acceptable and your feedback.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [], + "source": [ + "def evaluator_user_prompt(reply, message, history):\n", + " user_prompt = f\"Here's the conversation between the User and the Agent: \\n\\n{history}\\n\\n\"\n", + " user_prompt += f\"Here's the latest message from the User: \\n\\n{message}\\n\\n\"\n", + " user_prompt += f\"Here's the latest response from the Agent: \\n\\n{reply}\\n\\n\"\n", + " user_prompt += f\"Please evaluate the response, replying with whether it is acceptable and your feedback.\"\n", + " return user_prompt" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "gemini = OpenAI(\n", + " api_key=os.getenv(\"GOOGLE_API_KEY\"), \n", + " base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [], + "source": [ + "def evaluate(reply, message, history) -> Evaluation:\n", + "\n", + " messages = [{\"role\": \"system\", \"content\": evaluator_system_prompt}] + [{\"role\": \"user\", \"content\": evaluator_user_prompt(reply, message, history)}]\n", + " response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=messages, response_format=Evaluation)\n", + " return response.choices[0].message.parsed" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [], + "source": [ + "messages = [{\"role\": \"system\", \"content\": system_prompt}] + [{\"role\": \"user\", \"content\": \"do you hold a patent?\"}]\n", + "response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n", + "reply = response.choices[0].message.content" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'As of now, I do not hold any patents. My focus has been primarily on data science and analytics projects, particularly in the areas of machine learning and natural language processing. If you have any questions about my work or experience, feel free to ask!'" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reply" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Evaluation(is_acceptable=True, feedback=\"The response is acceptable. It's a straightforward and honest answer, and it directs the conversation back to Arnav's area of expertise.\")" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "evaluate(reply, \"do you hold a patent?\", messages[:1])" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [], + "source": [ + "def rerun(reply, message, history, feedback):\n", + " updated_system_prompt = system_prompt + f\"\\n\\n## Previous answer rejected\\nYou just tried to reply, but the quality control rejected your reply\\n\"\n", + " updated_system_prompt += f\"## Your attempted answer:\\n{reply}\\n\\n\"\n", + " updated_system_prompt += f\"## Reason for rejection:\\n{feedback}\\n\\n\"\n", + " messages = [{\"role\": \"system\", \"content\": updated_system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n", + " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n", + " return response.choices[0].message.content" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "def chat(message, history):\n", + " if \"patent\" in message:\n", + " system = system_prompt + \"\\n\\nEverything in your reply needs to be in pig latin - \\\n", + " it is mandatory that you respond only and entirely in pig latin\"\n", + " else:\n", + " system = system_prompt\n", + " messages = [{\"role\": \"system\", \"content\": system}] + history + [{\"role\": \"user\", \"content\": message}]\n", + " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n", + " reply =response.choices[0].message.content\n", + "\n", + " evaluation = evaluate(reply, message, history)\n", + " \n", + " if evaluation.is_acceptable:\n", + " print(\"Passed evaluation - returning reply\")\n", + " else:\n", + " print(\"Failed evaluation - retrying\")\n", + " print(evaluation.feedback)\n", + " reply = rerun(reply, message, history, evaluation.feedback) \n", + " return reply" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "* Running on local URL: http://127.0.0.1:7863\n", + "* To create a public link, set `share=True` in `launch()`.\n" + ] + }, + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Passed evaluation - returning reply\n", + "Failed evaluation - retrying\n", + "The response is not acceptable because it speaks in gibberish. This is not professional, helpful, or engaging.\n" + ] + } + ], + "source": [ + "gr.ChatInterface(chat, type=\"messages\").launch()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/4_lab4.ipynb b/4_lab4.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..53e66fe711a54215dfce1ca0c7e8744fcbb94720 --- /dev/null +++ b/4_lab4.ipynb @@ -0,0 +1,540 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The first big project - Professionally You!\n", + "\n", + "### And, Tool use.\n", + "\n", + "### But first: introducing Pushover\n", + "\n", + "Pushover is a nifty tool for sending Push Notifications to your phone.\n", + "\n", + "It's super easy to set up and install!\n", + "\n", + "Simply visit https://pushover.net/ and sign up for a free account, and create your API keys.\n", + "\n", + "As student Ron pointed out (thank you Ron!) there are actually 2 tokens to create in Pushover: \n", + "1. The User token which you get from the home page of Pushover\n", + "2. The Application token which you get by going to https://pushover.net/apps/build and creating an app \n", + "\n", + "(This is so you could choose to organize your push notifications into different apps in the future.)\n", + "\n", + "\n", + "Add to your `.env` file:\n", + "```\n", + "PUSHOVER_USER=put_your_user_token_here\n", + "PUSHOVER_TOKEN=put_the_application_level_token_here\n", + "```\n", + "\n", + "And install the Pushover app on your phone." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# imports\n", + "\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "import json\n", + "import os\n", + "import requests\n", + "from pypdf import PdfReader\n", + "import gradio as gr" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# The usual start\n", + "\n", + "load_dotenv(override=True)\n", + "openai = OpenAI()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# For pushover\n", + "\n", + "pushover_user = os.getenv(\"PUSHOVER_USER\")\n", + "pushover_token = os.getenv(\"PUSHOVER_TOKEN\")\n", + "pushover_url = \"https://api.pushover.net/1/messages.json\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def push(message):\n", + " print(f\"Push: {message}\")\n", + " payload = {\"user\": pushover_user, \"token\": pushover_token, \"message\": message}\n", + " requests.post(pushover_url, data=payload)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Push: HEY!!\n" + ] + } + ], + "source": [ + "push(\"HEY!!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def record_user_details(email, name=\"Name not provided\", notes=\"not provided\"):\n", + " push(f\"Recording interest from {name} with email {email} and notes {notes}\")\n", + " return {\"recorded\": \"ok\"}" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def record_unknown_question(question):\n", + " push(f\"Recording {question} asked that I couldn't answer\")\n", + " return {\"recorded\": \"ok\"}" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "record_user_details_json = {\n", + " \"name\": \"record_user_details\",\n", + " \"description\": \"Use this tool to record that a user is interested in being in touch and provided an email address\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"email\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"The email address of this user\"\n", + " },\n", + " \"name\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"The user's name, if they provided it\"\n", + " }\n", + " ,\n", + " \"notes\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"Any additional information about the conversation that's worth recording to give context\"\n", + " }\n", + " },\n", + " \"required\": [\"email\"],\n", + " \"additionalProperties\": False\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "record_unknown_question_json = {\n", + " \"name\": \"record_unknown_question\",\n", + " \"description\": \"Always use this tool to record any question that couldn't be answered as you didn't know the answer\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"question\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"The question that couldn't be answered\"\n", + " },\n", + " },\n", + " \"required\": [\"question\"],\n", + " \"additionalProperties\": False\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "tools = [{\"type\": \"function\", \"function\": record_user_details_json},\n", + " {\"type\": \"function\", \"function\": record_unknown_question_json}]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'type': 'function',\n", + " 'function': {'name': 'record_user_details',\n", + " 'description': 'Use this tool to record that a user is interested in being in touch and provided an email address',\n", + " 'parameters': {'type': 'object',\n", + " 'properties': {'email': {'type': 'string',\n", + " 'description': 'The email address of this user'},\n", + " 'name': {'type': 'string',\n", + " 'description': \"The user's name, if they provided it\"},\n", + " 'notes': {'type': 'string',\n", + " 'description': \"Any additional information about the conversation that's worth recording to give context\"}},\n", + " 'required': ['email'],\n", + " 'additionalProperties': False}}},\n", + " {'type': 'function',\n", + " 'function': {'name': 'record_unknown_question',\n", + " 'description': \"Always use this tool to record any question that couldn't be answered as you didn't know the answer\",\n", + " 'parameters': {'type': 'object',\n", + " 'properties': {'question': {'type': 'string',\n", + " 'description': \"The question that couldn't be answered\"}},\n", + " 'required': ['question'],\n", + " 'additionalProperties': False}}}]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tools" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "# This function can take a list of tool calls, and run them. This is the IF statement!!\n", + "\n", + "def handle_tool_calls(tool_calls):\n", + " results = []\n", + " for tool_call in tool_calls:\n", + " tool_name = tool_call.function.name\n", + " arguments = json.loads(tool_call.function.arguments)\n", + " print(f\"Tool called: {tool_name}\", flush=True)\n", + "\n", + " # THE BIG IF STATEMENT!!!\n", + "\n", + " if tool_name == \"record_user_details\":\n", + " result = record_user_details(**arguments)\n", + " elif tool_name == \"record_unknown_question\":\n", + " result = record_unknown_question(**arguments)\n", + "\n", + " results.append({\"role\": \"tool\",\"content\": json.dumps(result),\"tool_call_id\": tool_call.id})\n", + " return results" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Push: Recording this is a really hard question asked that I couldn't answer\n" + ] + }, + { + "data": { + "text/plain": [ + "{'recorded': 'ok'}" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "globals()[\"record_unknown_question\"](\"this is a really hard question\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# This is a more elegant way that avoids the IF statement.\n", + "\n", + "def handle_tool_calls(tool_calls):\n", + " results = []\n", + " for tool_call in tool_calls:\n", + " tool_name = tool_call.function.name\n", + " arguments = json.loads(tool_call.function.arguments)\n", + " print(f\"Tool called: {tool_name}\", flush=True)\n", + " tool = globals().get(tool_name)\n", + " result = tool(**arguments) if tool else {}\n", + " results.append({\"role\": \"tool\",\"content\": json.dumps(result),\"tool_call_id\": tool_call.id})\n", + " return results" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "reader = PdfReader(\"me/Profile.pdf\")\n", + "linkedin = \"\"\n", + "for page in reader.pages:\n", + " text = page.extract_text()\n", + " if text:\n", + " linkedin += text\n", + "\n", + "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n", + " summary = f.read()\n", + "\n", + "name = \"Arnav Agrawal\"" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n", + "particularly questions related to {name}'s career, background, skills and experience. \\\n", + "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n", + "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n", + "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n", + "If you don't know the answer to any question, use your record_unknown_question tool to record the question that you couldn't answer, even if it's about something trivial or unrelated to career. \\\n", + "If the user is engaging in discussion, try to steer them towards getting in touch via email; ask for their email and record it using your record_user_details tool. \"\n", + "\n", + "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n", + "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "def chat(message, history):\n", + " messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n", + " done = False\n", + " while not done:\n", + "\n", + " # This is the call to the LLM - see that we pass in the tools json\n", + "\n", + " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages, tools=tools)\n", + "\n", + " finish_reason = response.choices[0].finish_reason\n", + " \n", + " # If the LLM wants to call a tool, we do that!\n", + " \n", + " if finish_reason==\"tool_calls\":\n", + " message = response.choices[0].message\n", + " tool_calls = message.tool_calls\n", + " results = handle_tool_calls(tool_calls)\n", + " messages.append(message)\n", + " messages.extend(results)\n", + " else:\n", + " done = True\n", + " return response.choices[0].message.content" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "* Running on local URL: http://127.0.0.1:7860\n", + "* To create a public link, set `share=True` in `launch()`.\n" + ] + }, + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tool called: record_user_details\n", + "Push: Recording interest from John with email john.doe@email.com and notes Interested in connecting with Arnav Agrawal.\n", + "Tool called: record_unknown_question\n", + "Push: Recording What is your age? asked that I couldn't answer\n" + ] + } + ], + "source": [ + "gr.ChatInterface(chat, type=\"messages\").launch()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## And now for deployment\n", + "\n", + "This code is in `app.py`\n", + "\n", + "We will deploy to HuggingFace Spaces. Thank you student Robert M for improving these instructions.\n", + "\n", + "Before you start: remember to update the files in the \"me\" directory - your LinkedIn profile and summary.txt - so that it talks about you! \n", + "Also check that there's no README file within the 1_foundations directory. If there is one, please delete it. The deploy process creates a new README file in this directory for you.\n", + "\n", + "1. Visit https://huggingface.co and set up an account \n", + "2. From the Avatar menu on the top right, choose Access Tokens. Choose \"Create New Token\". Give it WRITE permissions.\n", + "3. Take this token and add it to your .env file: `HF_TOKEN=hf_xxx` and see note below if this token doesn't seem to get picked up during deployment \n", + "4. From the 1_foundations folder, enter: `uv run gradio deploy` and if for some reason this still wants you to enter your HF token, then interrupt it with ctrl+c and run this instead: `uv run dotenv -f ../.env run -- uv run gradio deploy` which forces your keys to all be set as environment variables \n", + "5. Follow its instructions: name it \"career_conversation\", specify app.py, choose cpu-basic as the hardware, say Yes to needing to supply secrets, provide your openai api key, your pushover user and token, and say \"no\" to github actions. \n", + "\n", + "#### Extra note about the HuggingFace token\n", + "\n", + "A couple of students have mentioned the HuggingFace doesn't detect their token, even though it's in the .env file. Here are things to try: \n", + "1. Restart Cursor \n", + "2. Rerun load_dotenv(override=True) and use a new terminal (the + button on the top right of the Terminal) \n", + "3. In the Terminal, run this before the gradio deploy: `$env:HF_TOKEN = \"hf_XXXX\"` \n", + "Thank you James and Martins for these tips. \n", + "\n", + "#### More about these secrets:\n", + "\n", + "If you're confused by what's going on with these secrets: it just wants you to enter the key name and value for each of your secrets -- so you would enter: \n", + "`OPENAI_API_KEY` \n", + "Followed by: \n", + "`sk-proj-...` \n", + "\n", + "And if you don't want to set secrets this way, or something goes wrong with it, it's no problem - you can change your secrets later: \n", + "1. Log in to HuggingFace website \n", + "2. Go to your profile screen via the Avatar menu on the top right \n", + "3. Select the Space you deployed \n", + "4. Click on the Settings wheel on the top right \n", + "5. You can scroll down to change your secrets, delete the space, etc.\n", + "\n", + "#### And now you should be deployed!\n", + "\n", + "Here is mine: https://huggingface.co/spaces/ed-donner/Career_Conversation\n", + "\n", + "I just got a push notification that a student asked me how they can become President of their country 😂😂\n", + "\n", + "For more information on deployment:\n", + "\n", + "https://www.gradio.app/guides/sharing-your-app#hosting-on-hf-spaces\n", + "\n", + "To delete your Space in the future: \n", + "1. Log in to HuggingFace\n", + "2. From the Avatar menu, select your profile\n", + "3. Click on the Space itself and select the settings wheel on the top right\n", + "4. Scroll to the Delete section at the bottom\n", + "5. ALSO: delete the README file that Gradio may have created inside this 1_foundations folder (otherwise it won't ask you the questions the next time you do a gradio deploy)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Exercise

\n", + " • First and foremost, deploy this for yourself! It's a real, valuable tool - the future resume..
\n", + " • Next, improve the resources - add better context about yourself. If you know RAG, then add a knowledge base about you.
\n", + " • Add in more tools! You could have a SQL database with common Q&A that the LLM could read and write from?
\n", + " • Bring in the Evaluator from the last lab, and add other Agentic patterns.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Commercial implications

\n", + " Aside from the obvious (your career alter-ego) this has business applications in any situation where you need an AI assistant with domain expertise and an ability to interact with the real world.\n", + " \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.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/README.md b/README.md index 48fb8a531cabab204d20256089dc503b5250654d..bb6ae32d9b5e4ed4f0ffaaf6aaab921d29e2e381 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,6 @@ --- -title: 1 Foundations -emoji: 👀 -colorFrom: gray -colorTo: red -sdk: gradio -sdk_version: 5.33.2 +title: 1_foundations app_file: app.py -pinned: false +sdk: gradio +sdk_version: 5.33.1 --- - -Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..62a36d93a91c1fb278b9b6ab9b3437e5171c48bd --- /dev/null +++ b/app.py @@ -0,0 +1,134 @@ +from dotenv import load_dotenv +from openai import OpenAI +import json +import os +import requests +from pypdf import PdfReader +import gradio as gr + + +load_dotenv(override=True) + +def push(text): + requests.post( + "https://api.pushover.net/1/messages.json", + data={ + "token": os.getenv("PUSHOVER_TOKEN"), + "user": os.getenv("PUSHOVER_USER"), + "message": text, + } + ) + + +def record_user_details(email, name="Name not provided", notes="not provided"): + push(f"Recording {name} with email {email} and notes {notes}") + return {"recorded": "ok"} + +def record_unknown_question(question): + push(f"Recording {question}") + return {"recorded": "ok"} + +record_user_details_json = { + "name": "record_user_details", + "description": "Use this tool to record that a user is interested in being in touch and provided an email address", + "parameters": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "The email address of this user" + }, + "name": { + "type": "string", + "description": "The user's name, if they provided it" + } + , + "notes": { + "type": "string", + "description": "Any additional information about the conversation that's worth recording to give context" + } + }, + "required": ["email"], + "additionalProperties": False + } +} + +record_unknown_question_json = { + "name": "record_unknown_question", + "description": "Always use this tool to record any question that couldn't be answered as you didn't know the answer", + "parameters": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "The question that couldn't be answered" + }, + }, + "required": ["question"], + "additionalProperties": False + } +} + +tools = [{"type": "function", "function": record_user_details_json}, + {"type": "function", "function": record_unknown_question_json}] + + +class Me: + + def __init__(self): + self.openai = OpenAI() + self.name = "Ed Donner" + reader = PdfReader("me/linkedin.pdf") + self.linkedin = "" + for page in reader.pages: + text = page.extract_text() + if text: + self.linkedin += text + with open("me/summary.txt", "r", encoding="utf-8") as f: + self.summary = f.read() + + + def handle_tool_call(self, tool_calls): + results = [] + for tool_call in tool_calls: + tool_name = tool_call.function.name + arguments = json.loads(tool_call.function.arguments) + print(f"Tool called: {tool_name}", flush=True) + tool = globals().get(tool_name) + result = tool(**arguments) if tool else {} + results.append({"role": "tool","content": json.dumps(result),"tool_call_id": tool_call.id}) + return results + + def system_prompt(self): + system_prompt = f"You are acting as {self.name}. You are answering questions on {self.name}'s website, \ +particularly questions related to {self.name}'s career, background, skills and experience. \ +Your responsibility is to represent {self.name} for interactions on the website as faithfully as possible. \ +You are given a summary of {self.name}'s background and LinkedIn profile which you can use to answer questions. \ +Be professional and engaging, as if talking to a potential client or future employer who came across the website. \ +If you don't know the answer to any question, use your record_unknown_question tool to record the question that you couldn't answer, even if it's about something trivial or unrelated to career. \ +If the user is engaging in discussion, try to steer them towards getting in touch via email; ask for their email and record it using your record_user_details tool. " + + system_prompt += f"\n\n## Summary:\n{self.summary}\n\n## LinkedIn Profile:\n{self.linkedin}\n\n" + system_prompt += f"With this context, please chat with the user, always staying in character as {self.name}." + return system_prompt + + def chat(self, message, history): + messages = [{"role": "system", "content": self.system_prompt()}] + history + [{"role": "user", "content": message}] + done = False + while not done: + response = self.openai.chat.completions.create(model="gpt-4o-mini", messages=messages, tools=tools) + if response.choices[0].finish_reason=="tool_calls": + message = response.choices[0].message + tool_calls = message.tool_calls + results = self.handle_tool_call(tool_calls) + messages.append(message) + messages.extend(results) + else: + done = True + return response.choices[0].message.content + + +if __name__ == "__main__": + me = Me() + gr.ChatInterface(me.chat, type="messages").launch() + \ No newline at end of file diff --git a/community_contributions/1_lab1_Mudassar.ipynb b/community_contributions/1_lab1_Mudassar.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..8110823029e3878dea8a0fa64a62df626852a96f --- /dev/null +++ b/community_contributions/1_lab1_Mudassar.ipynb @@ -0,0 +1,260 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# First Agentic AI workflow with OPENAI" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### And please do remember to contact me if I can help\n", + "\n", + "And I love to connect: https://www.linkedin.com/in/muhammad-mudassar-a65645192/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import re\n", + "from openai import OpenAI\n", + "from dotenv import load_dotenv\n", + "from IPython.display import Markdown, display" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "openai_api_key=os.getenv(\"OPENAI_API_KEY\")\n", + "if openai_api_key:\n", + " print(f\"openai api key exists and begins {openai_api_key[:8]}\")\n", + "else:\n", + " print(\"OpenAI API Key not set - please head to the troubleshooting guide in the gui\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Workflow with OPENAI" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "openai=OpenAI()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "message = [{'role':'user','content':\"what is 2+3?\"}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n", + "print(response.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n", + "message=[{'role':'user','content':question}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response=openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n", + "question=response.choices[0].message.content\n", + "print(f\"Answer: {question}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [], + "source": [ + "message=[{'role':'user','content':question}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response=openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n", + "answer = response.choices[0].message.content\n", + "print(f\"Answer: {answer}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# convert \\[ ... \\] to $$ ... $$, to properly render Latex\n", + "converted_answer = re.sub(r'\\\\[\\[\\]]', '$$', answer)\n", + "display(Markdown(converted_answer))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " Now try this commercial application:
\n", + " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.
\n", + " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.
\n", + " Finally have 3 third LLM call propose the Agentic AI solution.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [], + "source": [ + "message = [{'role':'user','content':\"give me a business area related to ecommerce that might be worth exploring for a agentic opportunity.\"}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n", + "business_area = response.choices[0].message.content\n", + "business_area" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "message = business_area + \"present a pain-point in that industry - something challenging that might be ripe for an agentic solutions.\"\n", + "message" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "message = [{'role': 'user', 'content': message}]\n", + "response = openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n", + "question=response.choices[0].message.content\n", + "question" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "message=[{'role':'user','content':question}]\n", + "response=openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n", + "answer=response.choices[0].message.content\n", + "print(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "display(Markdown(answer))" + ] + } + ], + "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.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/1_lab1_Thanh.ipynb b/community_contributions/1_lab1_Thanh.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..b8aef05d4e4e2b3d2c1d7bd6a61252e72c264696 --- /dev/null +++ b/community_contributions/1_lab1_Thanh.ipynb @@ -0,0 +1,165 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Welcome to the start of your adventure in Agentic AI" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### And please do remember to contact me if I can help\n", + "\n", + "And I love to connect: https://www.linkedin.com/in/eddonner/\n", + "\n", + "\n", + "### New to Notebooks like this one? Head over to the guides folder!\n", + "\n", + "Just to check you've already added the Python and Jupyter extensions to Cursor, if not already installed:\n", + "- Open extensions (View >> extensions)\n", + "- Search for python, and when the results show, click on the ms-python one, and Install it if not already installed\n", + "- Search for jupyter, and when the results show, click on the Microsoft one, and Install it if not already installed \n", + "Then View >> Explorer to bring back the File Explorer.\n", + "\n", + "And then:\n", + "1. Click where it says \"Select Kernel\" near the top right, and select the option called `.venv (Python 3.12.9)` or similar, which should be the first choice or the most prominent choice. You may need to choose \"Python Environments\" first.\n", + "2. Click in each \"cell\" below, starting with the cell immediately below this text, and press Shift+Enter to run\n", + "3. Enjoy!\n", + "\n", + "After you click \"Select Kernel\", if there is no option like `.venv (Python 3.12.9)` then please do the following: \n", + "1. On Mac: From the Cursor menu, choose Settings >> VS Code Settings (NOTE: be sure to select `VSCode Settings` not `Cursor Settings`); \n", + "On Windows PC: From the File menu, choose Preferences >> VS Code Settings(NOTE: be sure to select `VSCode Settings` not `Cursor Settings`) \n", + "2. In the Settings search bar, type \"venv\" \n", + "3. In the field \"Path to folder with a list of Virtual Environments\" put the path to the project root, like C:\\Users\\username\\projects\\agents (on a Windows PC) or /Users/username/projects/agents (on Mac or Linux). \n", + "And then try again.\n", + "\n", + "Having problems with missing Python versions in that list? Have you ever used Anaconda before? It might be interferring. Quit Cursor, bring up a new command line, and make sure that your Anaconda environment is deactivated: \n", + "`conda deactivate` \n", + "And if you still have any problems with conda and python versions, it's possible that you will need to run this too: \n", + "`conda config --set auto_activate_base false` \n", + "and then from within the Agents directory, you should be able to run `uv python list` and see the Python 3.12 version." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from dotenv import load_dotenv\n", + "load_dotenv()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Check the keys\n", + "import google.generativeai as genai\n", + "import os\n", + "genai.configure(api_key=os.getenv('GOOGLE_API_KEY'))\n", + "model = genai.GenerativeModel(model_name=\"gemini-1.5-flash\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a list of messages in the familiar Gemini GenAI format\n", + "\n", + "response = model.generate_content([\"2+2=?\"])\n", + "response.text" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# And now - let's ask for a question:\n", + "\n", + "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n", + "\n", + "response = model.generate_content([question])\n", + "print(response.text)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Markdown, display\n", + "\n", + "display(Markdown(response.text))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Congratulations!\n", + "\n", + "That was a small, simple step in the direction of Agentic AI, with your new environment!\n", + "\n", + "Next time things get more interesting..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# First create the messages:\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": \"Something here\"}]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response =\n", + "\n", + "# Then read the business idea:\n", + "\n", + "business_idea = response.\n", + "\n", + "# And repeat!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "llm_projects", + "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.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/1_lab1_gemini.ipynb b/community_contributions/1_lab1_gemini.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a00c1098c11d5299f85cc2b6a04227d4bd2de5f8 --- /dev/null +++ b/community_contributions/1_lab1_gemini.ipynb @@ -0,0 +1,306 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Welcome to the start of your adventure in Agentic AI" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Are you ready for action??

\n", + " Have you completed all the setup steps in the setup folder?
\n", + " Have you checked out the guides in the guides folder?
\n", + " Well in that case, you're ready!!\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Treat these labs as a resource

\n", + " I push updates to the code regularly. When people ask questions or have problems, I incorporate it in the code, adding more examples or improved commentary. As a result, you'll notice that the code below isn't identical to the videos. Everything from the videos is here; but in addition, I've added more steps and better explanations. Consider this like an interactive book that accompanies the lectures.\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### And please do remember to contact me if I can help\n", + "\n", + "And I love to connect: https://www.linkedin.com/in/eddonner/\n", + "\n", + "\n", + "### New to Notebooks like this one? Head over to the guides folder!\n", + "\n", + "Just to check you've already added the Python and Jupyter extensions to Cursor, if not already installed:\n", + "- Open extensions (View >> extensions)\n", + "- Search for python, and when the results show, click on the ms-python one, and Install it if not already installed\n", + "- Search for jupyter, and when the results show, click on the Microsoft one, and Install it if not already installed \n", + "Then View >> Explorer to bring back the File Explorer.\n", + "\n", + "And then:\n", + "1. Run `uv add google-genai` to install the Google Gemini library. (If you had started your environment before running this command, you will need to restart your environment in the Jupyter notebook.)\n", + "2. Click where it says \"Select Kernel\" near the top right, and select the option called `.venv (Python 3.12.9)` or similar, which should be the first choice or the most prominent choice. You may need to choose \"Python Environments\" first.\n", + "3. Click in each \"cell\" below, starting with the cell immediately below this text, and press Shift+Enter to run\n", + "4. Enjoy!\n", + "\n", + "After you click \"Select Kernel\", if there is no option like `.venv (Python 3.12.9)` then please do the following: \n", + "1. From the Cursor menu, choose Settings >> VSCode Settings (NOTE: be sure to select `VSCode Settings` not `Cursor Settings`) \n", + "2. In the Settings search bar, type \"venv\" \n", + "3. In the field \"Path to folder with a list of Virtual Environments\" put the path to the project root, like C:\\Users\\username\\projects\\agents (on a Windows PC) or /Users/username/projects/agents (on Mac or Linux). \n", + "And then try again.\n", + "\n", + "Having problems with missing Python versions in that list? Have you ever used Anaconda before? It might be interferring. Quit Cursor, bring up a new command line, and make sure that your Anaconda environment is deactivated: \n", + "`conda deactivate` \n", + "And if you still have any problems with conda and python versions, it's possible that you will need to run this too: \n", + "`conda config --set auto_activate_base false` \n", + "and then from within the Agents directory, you should be able to run `uv python list` and see the Python 3.12 version." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# First let's do an import\n", + "from dotenv import load_dotenv\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Next it's time to load the API keys into environment variables\n", + "\n", + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Check the keys\n", + "\n", + "import os\n", + "gemini_api_key = os.getenv('GEMINI_API_KEY')\n", + "\n", + "if gemini_api_key:\n", + " print(f\"Gemini API Key exists and begins {gemini_api_key[:8]}\")\n", + "else:\n", + " print(\"Gemini API Key not set - please head to the troubleshooting guide in the guides folder\")\n", + " \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# And now - the all important import statement\n", + "# If you get an import error - head over to troubleshooting guide\n", + "\n", + "from google import genai" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# And now we'll create an instance of the Gemini GenAI class\n", + "# If you're not sure what it means to create an instance of a class - head over to the guides folder!\n", + "# If you get a NameError - head over to the guides folder to learn about NameErrors\n", + "\n", + "client = genai.Client(api_key=gemini_api_key)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a list of messages in the familiar Gemini GenAI format\n", + "\n", + "messages = [\"What is 2+2?\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# And now call it! Any problems, head to the troubleshooting guide\n", + "\n", + "response = client.models.generate_content(\n", + " model=\"gemini-2.0-flash\", contents=messages\n", + ")\n", + "\n", + "print(response.text)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Lets no create a challenging question\n", + "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n", + "\n", + "# Ask the the model\n", + "response = client.models.generate_content(\n", + " model=\"gemini-2.0-flash\", contents=question\n", + ")\n", + "\n", + "question = response.text\n", + "\n", + "print(question)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ask the models generated question to the model\n", + "response = client.models.generate_content(\n", + " model=\"gemini-2.0-flash\", contents=question\n", + ")\n", + "\n", + "# Extract the answer from the response\n", + "answer = response.text\n", + "\n", + "# Debug log the answer\n", + "print(answer)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Markdown, display\n", + "\n", + "# Nicely format the answer using Markdown\n", + "display(Markdown(answer))\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Congratulations!\n", + "\n", + "That was a small, simple step in the direction of Agentic AI, with your new environment!\n", + "\n", + "Next time things get more interesting..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Exercise

\n", + " Now try this commercial application:
\n", + " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.
\n", + " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.
\n", + " Finally have 3 third LLM call propose the Agentic AI solution.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# First create the messages:\n", + "\n", + "\n", + "messages = [\"Something here\"]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response =\n", + "\n", + "# Then read the business idea:\n", + "\n", + "business_idea = response.\n", + "\n", + "# And repeat!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "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.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/1_lab1_groq_llama.ipynb b/community_contributions/1_lab1_groq_llama.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3c5cc63dba4406970311c380d1579302b17b151a --- /dev/null +++ b/community_contributions/1_lab1_groq_llama.ipynb @@ -0,0 +1,296 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# First Agentic AI workflow with Groq and Llama-3.3 LLM(Free of cost) " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# First let's do an import\n", + "from dotenv import load_dotenv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Next it's time to load the API keys into environment variables\n", + "\n", + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Check the Groq API key\n", + "\n", + "import os\n", + "groq_api_key = os.getenv('GROQ_API_KEY')\n", + "\n", + "if groq_api_key:\n", + " print(f\"GROQ API Key exists and begins {groq_api_key[:8]}\")\n", + "else:\n", + " print(\"GROQ API Key not set\")\n", + " \n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# And now - the all important import statement\n", + "# If you get an import error - head over to troubleshooting guide\n", + "\n", + "from groq import Groq" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a Groq instance\n", + "groq = Groq()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a list of messages in the familiar Groq format\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": \"What is 2+2?\"}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# And now call it!\n", + "\n", + "response = groq.chat.completions.create(model='llama-3.3-70b-versatile', messages=messages)\n", + "print(response.choices[0].message.content)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# And now - let's ask for a question:\n", + "\n", + "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n", + "messages = [{\"role\": \"user\", \"content\": question}]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ask it\n", + "response = groq.chat.completions.create(\n", + " model=\"llama-3.3-70b-versatile\",\n", + " messages=messages\n", + ")\n", + "\n", + "question = response.choices[0].message.content\n", + "\n", + "print(question)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# form a new messages list\n", + "messages = [{\"role\": \"user\", \"content\": question}]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ask it again\n", + "\n", + "response = groq.chat.completions.create(\n", + " model=\"llama-3.3-70b-versatile\",\n", + " messages=messages\n", + ")\n", + "\n", + "answer = response.choices[0].message.content\n", + "print(answer)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Markdown, display\n", + "\n", + "display(Markdown(answer))\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Exercise

\n", + " Now try this commercial application:
\n", + " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.
\n", + " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.
\n", + " Finally have 3 third LLM call propose the Agentic AI solution.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "# First create the messages:\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": \"Give me a business area that might be ripe for an Agentic AI solution.\"}]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response = groq.chat.completions.create(model='llama-3.3-70b-versatile', messages=messages)\n", + "\n", + "# Then read the business idea:\n", + "\n", + "business_idea = response.choices[0].message.content\n", + "\n", + "\n", + "# And repeat!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "display(Markdown(business_idea))" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "# Update the message with the business idea from previous step\n", + "messages = [{\"role\": \"user\", \"content\": \"What is the pain point in the business area of \" + business_idea + \"?\"}]" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "# Make the second call\n", + "response = groq.chat.completions.create(model='llama-3.3-70b-versatile', messages=messages)\n", + "# Read the pain point\n", + "pain_point = response.choices[0].message.content\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "display(Markdown(pain_point))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Make the third call\n", + "messages = [{\"role\": \"user\", \"content\": \"What is the Agentic AI solution for the pain point of \" + pain_point + \"?\"}]\n", + "response = groq.chat.completions.create(model='llama-3.3-70b-versatile', messages=messages)\n", + "# Read the agentic solution\n", + "agentic_solution = response.choices[0].message.content\n", + "display(Markdown(agentic_solution))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/1_lab1_open_router.ipynb b/community_contributions/1_lab1_open_router.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..67589aef4de7d2c5aeca76fdc5b148b6a8371887 --- /dev/null +++ b/community_contributions/1_lab1_open_router.ipynb @@ -0,0 +1,323 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Welcome to the start of your adventure in Agentic AI" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Are you ready for action??

\n", + " Have you completed all the setup steps in the setup folder?
\n", + " Have you checked out the guides in the guides folder?
\n", + " Well in that case, you're ready!!\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

This code is a live resource - keep an eye out for my updates

\n", + " I push updates regularly. As people ask questions or have problems, I add more examples and improve explanations. As a result, the code below might not be identical to the videos, as I've added more steps and better comments. Consider this like an interactive book that accompanies the lectures.

\n", + " I try to send emails regularly with important updates related to the course. You can find this in the 'Announcements' section of Udemy in the left sidebar. You can also choose to receive my emails via your Notification Settings in Udemy. I'm respectful of your inbox and always try to add value with my emails!\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### And please do remember to contact me if I can help\n", + "\n", + "And I love to connect: https://www.linkedin.com/in/eddonner/\n", + "\n", + "\n", + "### New to Notebooks like this one? Head over to the guides folder!\n", + "\n", + "Just to check you've already added the Python and Jupyter extensions to Cursor, if not already installed:\n", + "- Open extensions (View >> extensions)\n", + "- Search for python, and when the results show, click on the ms-python one, and Install it if not already installed\n", + "- Search for jupyter, and when the results show, click on the Microsoft one, and Install it if not already installed \n", + "Then View >> Explorer to bring back the File Explorer.\n", + "\n", + "And then:\n", + "1. Click where it says \"Select Kernel\" near the top right, and select the option called `.venv (Python 3.12.9)` or similar, which should be the first choice or the most prominent choice. You may need to choose \"Python Environments\" first.\n", + "2. Click in each \"cell\" below, starting with the cell immediately below this text, and press Shift+Enter to run\n", + "3. Enjoy!\n", + "\n", + "After you click \"Select Kernel\", if there is no option like `.venv (Python 3.12.9)` then please do the following: \n", + "1. On Mac: From the Cursor menu, choose Settings >> VS Code Settings (NOTE: be sure to select `VSCode Settings` not `Cursor Settings`); \n", + "On Windows PC: From the File menu, choose Preferences >> VS Code Settings(NOTE: be sure to select `VSCode Settings` not `Cursor Settings`) \n", + "2. In the Settings search bar, type \"venv\" \n", + "3. In the field \"Path to folder with a list of Virtual Environments\" put the path to the project root, like C:\\Users\\username\\projects\\agents (on a Windows PC) or /Users/username/projects/agents (on Mac or Linux). \n", + "And then try again.\n", + "\n", + "Having problems with missing Python versions in that list? Have you ever used Anaconda before? It might be interferring. Quit Cursor, bring up a new command line, and make sure that your Anaconda environment is deactivated: \n", + "`conda deactivate` \n", + "And if you still have any problems with conda and python versions, it's possible that you will need to run this too: \n", + "`conda config --set auto_activate_base false` \n", + "and then from within the Agents directory, you should be able to run `uv python list` and see the Python 3.12 version." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [], + "source": [ + "# First let's do an import\n", + "from dotenv import load_dotenv\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Next it's time to load the API keys into environment variables\n", + "\n", + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Check the keys\n", + "\n", + "import os\n", + "open_router_api_key = os.getenv('OPEN_ROUTER_API_KEY')\n", + "\n", + "if open_router_api_key:\n", + " print(f\"Open router API Key exists and begins {open_router_api_key[:8]}\")\n", + "else:\n", + " print(\"Open router API Key not set - please head to the troubleshooting guide in the setup folder\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": {}, + "outputs": [], + "source": [ + "from openai import OpenAI" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize the client to point at OpenRouter instead of OpenAI\n", + "# You can use the exact same OpenAI Python package—just swap the base_url!\n", + "client = OpenAI(\n", + " base_url=\"https://openrouter.ai/api/v1\",\n", + " api_key=open_router_api_key\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [], + "source": [ + "messages = [{\"role\": \"user\", \"content\": \"What is 2+2?\"}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "client = OpenAI(\n", + " base_url=\"https://openrouter.ai/api/v1\",\n", + " api_key=open_router_api_key\n", + ")\n", + "\n", + "resp = client.chat.completions.create(\n", + " # Select a model from https://openrouter.ai/models and provide the model name here\n", + " model=\"meta-llama/llama-3.3-8b-instruct:free\",\n", + " messages=messages\n", + ")\n", + "print(resp.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [], + "source": [ + "# And now - let's ask for a question:\n", + "\n", + "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n", + "messages = [{\"role\": \"user\", \"content\": question}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " model=\"meta-llama/llama-3.3-8b-instruct:free\",\n", + " messages=messages\n", + ")\n", + "\n", + "question = response.choices[0].message.content\n", + "\n", + "print(question)" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [], + "source": [ + "# form a new messages list\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": question}]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ask it again\n", + "\n", + "response = client.chat.completions.create(\n", + " model=\"meta-llama/llama-3.3-8b-instruct:free\",\n", + " messages=messages\n", + ")\n", + "\n", + "answer = response.choices[0].message.content\n", + "print(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Markdown, display\n", + "\n", + "display(Markdown(answer))\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Congratulations!\n", + "\n", + "That was a small, simple step in the direction of Agentic AI, with your new environment!\n", + "\n", + "Next time things get more interesting..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Exercise

\n", + " Now try this commercial application:
\n", + " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.
\n", + " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.
\n", + " Finally have 3 third LLM call propose the Agentic AI solution.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# First create the messages:\n", + "\n", + "\n", + "messages = [\"Something here\"]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response =\n", + "\n", + "# Then read the business idea:\n", + "\n", + "business_idea = response.\n", + "\n", + "# And repeat!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "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.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/1_lab2_Kaushik_Parallelization.ipynb b/community_contributions/1_lab2_Kaushik_Parallelization.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..1761a01e7c73e004fc64a4fe0b4f174bf37c4bc9 --- /dev/null +++ b/community_contributions/1_lab2_Kaushik_Parallelization.ipynb @@ -0,0 +1,355 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import json\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "from IPython.display import Markdown" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Refresh dot env" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "open_api_key = os.getenv(\"OPENAI_API_KEY\")\n", + "google_api_key = os.getenv(\"GOOGLE_API_KEY\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create initial query to get challange reccomendation" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "query = 'Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their intelligence. '\n", + "query += 'Answer only with the question, no explanation.'\n", + "\n", + "messages = [{'role':'user', 'content':query}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(messages)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Call openai gpt-4o-mini " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "openai = OpenAI()\n", + "\n", + "response = openai.chat.completions.create(\n", + " messages=messages,\n", + " model='gpt-4o-mini'\n", + ")\n", + "\n", + "challange = response.choices[0].message.content\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(challange)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "competitors = []\n", + "answers = []" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create messages with the challange query" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "messages = [{'role':'user', 'content':challange}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(messages)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!ollama pull llama3.2" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "from threading import Thread" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "def gpt_mini_processor():\n", + " modleName = 'gpt-4o-mini'\n", + " competitors.append(modleName)\n", + " response_gpt = openai.chat.completions.create(\n", + " messages=messages,\n", + " model=modleName\n", + " )\n", + " answers.append(response_gpt.choices[0].message.content)\n", + "\n", + "def gemini_processor():\n", + " gemini = OpenAI(api_key=google_api_key, base_url='https://generativelanguage.googleapis.com/v1beta/openai/')\n", + " modleName = 'gemini-2.0-flash'\n", + " competitors.append(modleName)\n", + " response_gemini = gemini.chat.completions.create(\n", + " messages=messages,\n", + " model=modleName\n", + " )\n", + " answers.append(response_gemini.choices[0].message.content)\n", + "\n", + "def llama_processor():\n", + " ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n", + " modleName = 'llama3.2'\n", + " competitors.append(modleName)\n", + " response_llama = ollama.chat.completions.create(\n", + " messages=messages,\n", + " model=modleName\n", + " )\n", + " answers.append(response_llama.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Paraller execution of LLM calls" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "thread1 = Thread(target=gpt_mini_processor)\n", + "thread2 = Thread(target=gemini_processor)\n", + "thread3 = Thread(target=llama_processor)\n", + "\n", + "thread1.start()\n", + "thread2.start()\n", + "thread3.start()\n", + "\n", + "thread1.join()\n", + "thread2.join()\n", + "thread3.join()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(competitors)\n", + "print(answers)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for competitor, answer in zip(competitors, answers):\n", + " print(f'Competitor:{competitor}\\n\\n{answer}')" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "together = ''\n", + "for index, answer in enumerate(answers):\n", + " together += f'# Response from competitor {index + 1}\\n\\n'\n", + " together += answer + '\\n\\n'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(together)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Prompt to judge the LLM results" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "to_judge = f'''You are judging a competition between {len(competitors)} competitors.\n", + "Each model has been given this question:\n", + "\n", + "{challange}\n", + "\n", + "Your job is to evaluate each response for clarity and strength of argument, and rank them in order of best to worst.\n", + "Respond with JSON, and only JSON, with the following format:\n", + "{{\"results\": [\"best competitor number\", \"second best competitor number\", \"third best competitor number\", ...]}}\n", + "\n", + "Here are the responses from each competitor:\n", + "\n", + "{together}\n", + "\n", + "Now respond with the JSON with the ranked order of the competitors, nothing else. Do not include markdown formatting or code blocks.\"\"\"\n", + "\n", + "'''" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "to_judge_message = [{'role':'user', 'content':to_judge}]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Execute o3-mini to analyze the LLM results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "openai = OpenAI()\n", + "response = openai.chat.completions.create(\n", + " messages=to_judge_message,\n", + " model='o3-mini'\n", + ")\n", + "result = response.choices[0].message.content\n", + "print(result)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "results_dict = json.loads(result)\n", + "ranks = results_dict[\"results\"]\n", + "for index, result in enumerate(ranks):\n", + " competitor = competitors[int(result)-1]\n", + " print(f\"Rank {index+1}: {competitor}\")" + ] + } + ], + "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.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/2_lab2_exercise.ipynb b/community_contributions/2_lab2_exercise.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..80b984cbf0c75ac234d09f4b07c0eebfe437e4c0 --- /dev/null +++ b/community_contributions/2_lab2_exercise.ipynb @@ -0,0 +1,336 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# From Judging to Synthesizing — Evolving Multi-Agent Patterns\n", + "\n", + "In the original 2_lab2.ipynb, we explored a powerful agentic design pattern: sending the same question to multiple large language models (LLMs), then using a separate “judge” agent to evaluate and rank their responses. This approach is valuable for identifying the single best answer among many, leveraging the strengths of ensemble reasoning and critical evaluation.\n", + "\n", + "However, selecting just one “winner” can leave valuable insights from other models untapped. To address this, I am shifting to a new agentic pattern in this notebook: the synthesizer/improver pattern. Instead of merely ranking responses, we will prompt a dedicated LLM to review all answers, extract the most compelling ideas from each, and synthesize them into a single, improved response. \n", + "\n", + "This approach aims to combine the collective intelligence of multiple models, producing an answer that is richer, more nuanced, and more robust than any individual response.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import json\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "from anthropic import Anthropic\n", + "from IPython.display import Markdown, display" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Print the key prefixes to help with any debugging\n", + "\n", + "openai_api_key = os.getenv('OPENAI_API_KEY')\n", + "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n", + "google_api_key = os.getenv('GOOGLE_API_KEY')\n", + "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n", + "groq_api_key = os.getenv('GROQ_API_KEY')\n", + "\n", + "if openai_api_key:\n", + " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n", + "else:\n", + " print(\"OpenAI API Key not set\")\n", + " \n", + "if anthropic_api_key:\n", + " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n", + "else:\n", + " print(\"Anthropic API Key not set (and this is optional)\")\n", + "\n", + "if google_api_key:\n", + " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n", + "else:\n", + " print(\"Google API Key not set (and this is optional)\")\n", + "\n", + "if deepseek_api_key:\n", + " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n", + "else:\n", + " print(\"DeepSeek API Key not set (and this is optional)\")\n", + "\n", + "if groq_api_key:\n", + " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n", + "else:\n", + " print(\"Groq API Key not set (and this is optional)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "request = \"Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their collective intelligence. \"\n", + "request += \"Answer only with the question, no explanation.\"\n", + "messages = [{\"role\": \"user\", \"content\": request}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "messages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "openai = OpenAI()\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages,\n", + ")\n", + "question = response.choices[0].message.content\n", + "print(question)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "teammates = []\n", + "answers = []\n", + "messages = [{\"role\": \"user\", \"content\": question}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The API we know well\n", + "\n", + "model_name = \"gpt-4o-mini\"\n", + "\n", + "response = openai.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "teammates.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Anthropic has a slightly different API, and Max Tokens is required\n", + "\n", + "model_name = \"claude-3-7-sonnet-latest\"\n", + "\n", + "claude = Anthropic()\n", + "response = claude.messages.create(model=model_name, messages=messages, max_tokens=1000)\n", + "answer = response.content[0].text\n", + "\n", + "display(Markdown(answer))\n", + "teammates.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n", + "model_name = \"gemini-2.0-flash\"\n", + "\n", + "response = gemini.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "teammates.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n", + "model_name = \"deepseek-chat\"\n", + "\n", + "response = deepseek.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "teammates.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n", + "model_name = \"llama-3.3-70b-versatile\"\n", + "\n", + "response = groq.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "teammates.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# So where are we?\n", + "\n", + "print(teammates)\n", + "print(answers)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# It's nice to know how to use \"zip\"\n", + "for teammate, answer in zip(teammates, answers):\n", + " print(f\"Teammate: {teammate}\\n\\n{answer}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "# Let's bring this together - note the use of \"enumerate\"\n", + "\n", + "together = \"\"\n", + "for index, answer in enumerate(answers):\n", + " together += f\"# Response from teammate {index+1}\\n\\n\"\n", + " together += answer + \"\\n\\n\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(together)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "formatter = f\"\"\"You are taking the nost interesting ideas fron {len(teammates)} teammates.\n", + "Each model has been given this question:\n", + "\n", + "{question}\n", + "\n", + "Your job is to evaluate each response for clarity and strength of argument, select the most relevant ideas and make a report, including a title, subtitles to separate sections, and quoting the LLM providing the idea.\n", + "From that, you will create a new improved answer.\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(formatter)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [], + "source": [ + "formatter_messages = [{\"role\": \"user\", \"content\": formatter}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "openai = OpenAI()\n", + "response = openai.chat.completions.create(\n", + " model=\"o3-mini\",\n", + " messages=formatter_messages,\n", + ")\n", + "results = response.choices[0].message.content\n", + "display(Markdown(results))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/2_lab2_six-thinking-hats-simulator.ipynb b/community_contributions/2_lab2_six-thinking-hats-simulator.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..dd40a9a4538a8655b07974b1ae121f8721de812c --- /dev/null +++ b/community_contributions/2_lab2_six-thinking-hats-simulator.ipynb @@ -0,0 +1,457 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Six Thinking Hats Simulator\n", + "\n", + "## Objective\n", + "This notebook implements a simulator of the Six Thinking Hats technique to evaluate and improve technological solutions. The simulator will:\n", + "\n", + "1. Use an LLM to generate an initial technological solution idea for a specific daily task in a company.\n", + "2. Apply the Six Thinking Hats methodology to analyze and improve the proposed solution.\n", + "3. Provide a comprehensive evaluation from different perspectives.\n", + "\n", + "## About the Six Thinking Hats Technique\n", + "\n", + "The Six Thinking Hats is a powerful technique developed by Edward de Bono that helps people look at problems and decisions from different perspectives. Each \"hat\" represents a different thinking approach:\n", + "\n", + "- **White Hat (Facts):** Focuses on available information, facts, and data.\n", + "- **Red Hat (Feelings):** Represents emotions, intuition, and gut feelings.\n", + "- **Black Hat (Critical):** Identifies potential problems, risks, and negative aspects.\n", + "- **Yellow Hat (Positive):** Looks for benefits, opportunities, and positive aspects.\n", + "- **Green Hat (Creative):** Encourages new ideas, alternatives, and possibilities.\n", + "- **Blue Hat (Process):** Manages the thinking process and ensures all perspectives are considered.\n", + "\n", + "In this simulator, we'll use these different perspectives to thoroughly evaluate and improve technological solutions proposed by an LLM." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import json\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "from anthropic import Anthropic\n", + "from IPython.display import Markdown, display" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Print the key prefixes to help with any debugging\n", + "\n", + "openai_api_key = os.getenv('OPENAI_API_KEY')\n", + "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n", + "google_api_key = os.getenv('GOOGLE_API_KEY')\n", + "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n", + "groq_api_key = os.getenv('GROQ_API_KEY')\n", + "\n", + "if openai_api_key:\n", + " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n", + "else:\n", + " print(\"OpenAI API Key not set\")\n", + " \n", + "if anthropic_api_key:\n", + " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n", + "else:\n", + " print(\"Anthropic API Key not set\")\n", + "\n", + "if google_api_key:\n", + " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n", + "else:\n", + " print(\"Google API Key not set\")\n", + "\n", + "if deepseek_api_key:\n", + " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n", + "else:\n", + " print(\"DeepSeek API Key not set\")\n", + "\n", + "if groq_api_key:\n", + " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n", + "else:\n", + " print(\"Groq API Key not set\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "request = \"Generate a technological solution to solve a specific workplace challenge. Choose an employee role, in a specific industry, and identify a time-consuming or error-prone daily task they face. Then, create an innovative yet practical technological solution that addresses this challenge. Include what technologies it uses (AI, automation, etc.), how it integrates with existing systems, its key benefits, and basic implementation requirements. Keep your solution realistic with current technology. \"\n", + "request += \"Answer only with the question, no explanation.\"\n", + "messages = [{\"role\": \"user\", \"content\": request}]\n", + "\n", + "openai = OpenAI()\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages,\n", + ")\n", + "question = response.choices[0].message.content\n", + "print(question)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "validation_prompt = f\"\"\"Validate and improve the following technological solution. For each iteration, check if the solution meets these criteria:\n", + "\n", + "1. Clarity:\n", + " - Is the problem clearly defined?\n", + " - Is the solution clearly explained?\n", + " - Are the technical components well-described?\n", + "\n", + "2. Specificity:\n", + " - Are there specific examples or use cases?\n", + " - Are the technologies and tools specifically named?\n", + " - Are the implementation steps detailed?\n", + "\n", + "3. Context:\n", + " - Is the industry/company context clear?\n", + " - Are the user roles and needs well-defined?\n", + " - Is the current workflow/problem well-described?\n", + "\n", + "4. Constraints:\n", + " - Are there clear technical limitations?\n", + " - Are there budget/time constraints mentioned?\n", + " - Are there integration requirements specified?\n", + "\n", + "If any of these criteria are not met, improve the solution by:\n", + "1. Adding missing details\n", + "2. Clarifying ambiguous points\n", + "3. Providing more specific examples\n", + "4. Including relevant constraints\n", + "\n", + "Here is the technological solution to validate and improve:\n", + "{question} \n", + "Provide an improved version that addresses any missing or unclear aspects. If this is the 5th iteration, return the final improved version without further changes.\n", + "\n", + "Response only with the Improved Solution:\n", + "[Your improved solution here]\"\"\"\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": validation_prompt}]\n", + "\n", + "response = openai.chat.completions.create(model=\"gpt-4o\", messages=messages)\n", + "question = response.choices[0].message.content\n", + "\n", + "display(Markdown(question))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "In this section, we will ask each AI model to analyze a technological solution using the Six Thinking Hats methodology. Each model will:\n", + "\n", + "1. First generate a technological solution for a workplace challenge\n", + "2. Then analyze that solution using each of the Six Thinking Hats\n", + "\n", + "Each model will provide:\n", + "1. An initial technological solution\n", + "2. A structured analysis using all six thinking hats\n", + "3. A final recommendation based on the comprehensive analysis\n", + "\n", + "This approach will allow us to:\n", + "- Compare how different models apply the Six Thinking Hats methodology\n", + "- Identify patterns and differences in their analytical approaches\n", + "- Gather diverse perspectives on the same solution\n", + "- Create a rich, multi-faceted evaluation of each proposed technological solution\n", + "\n", + "The responses will be collected and displayed below, showing how each model applies the Six Thinking Hats methodology to evaluate and improve the proposed solutions." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "models = []\n", + "answers = []\n", + "combined_question = f\" Analyze the technological solution prposed in {question} using the Six Thinking Hats methodology. For each hat, provide a detailed analysis. Finally, provide a comprehensive recommendation based on all the above analyses.\"\n", + "messages = [{\"role\": \"user\", \"content\": combined_question}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# GPT thinking process\n", + "\n", + "model_name = \"gpt-4o\"\n", + "\n", + "\n", + "response = openai.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "models.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Claude thinking process\n", + "\n", + "model_name = \"claude-3-7-sonnet-latest\"\n", + "\n", + "claude = Anthropic()\n", + "response = claude.messages.create(model=model_name, messages=messages, max_tokens=1000)\n", + "answer = response.content[0].text\n", + "\n", + "display(Markdown(answer))\n", + "models.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Gemini thinking process\n", + "\n", + "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n", + "model_name = \"gemini-2.0-flash\"\n", + "\n", + "response = gemini.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "models.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Deepseek thinking process\n", + "\n", + "deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n", + "model_name = \"deepseek-chat\"\n", + "\n", + "response = deepseek.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "models.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Groq thinking process\n", + "\n", + "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n", + "model_name = \"llama-3.3-70b-versatile\"\n", + "\n", + "response = groq.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "models.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!ollama pull llama3.2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ollama thinking process\n", + "\n", + "ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n", + "model_name = \"llama3.2\"\n", + "\n", + "response = ollama.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "models.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for model, answer in zip(models, answers):\n", + " print(f\"Model: {model}\\n\\n{answer}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next Step: Solution Synthesis and Enhancement\n", + "\n", + "**Best Recommendation Selection and Extended Solution Development**\n", + "\n", + "After applying the Six Thinking Hats analysis to evaluate the initial technological solution from multiple perspectives, the simulator will:\n", + "\n", + "1. **Synthesize Analysis Results**: Compile insights from all six thinking perspectives (White, Red, Black, Yellow, Green, and Blue hats) to identify the most compelling recommendations and improvements.\n", + "\n", + "2. **Select Optimal Recommendation**: Using a weighted evaluation system that considers feasibility, impact, and alignment with organizational goals, the simulator will identify and present the single best recommendation that emerged from the Six Thinking Hats analysis.\n", + "\n", + "3. **Generate Extended Solution**: Building upon the selected best recommendation, the simulator will create a comprehensive, enhanced version of the original technological solution that incorporates:\n", + " - Key insights from the critical analysis (Black Hat)\n", + " - Positive opportunities identified (Yellow Hat)\n", + " - Creative alternatives and innovations (Green Hat)\n", + " - Factual considerations and data requirements (White Hat)\n", + " - User experience and emotional factors (Red Hat)\n", + "\n", + "4. **Multi-Model Enhancement**: To further strengthen the solution, the simulator will leverage additional AI models or perspectives to provide supplementary recommendations that complement the Six Thinking Hats analysis, offering a more robust and well-rounded final technological solution.\n", + "\n", + "This step transforms the analytical insights into actionable improvements, delivering a refined solution that has been thoroughly evaluated and enhanced through structured critical thinking." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "together = \"\"\n", + "for index, answer in enumerate(answers):\n", + " together += f\"# Response from model {index+1}\\n\\n\"\n", + " together += answer + \"\\n\\n\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Markdown, display\n", + "import re\n", + "\n", + "print(f\"Each model has been given this technological solution to analyze: {question}\")\n", + "\n", + "# First, get the best individual response\n", + "judge_prompt = f\"\"\"\n", + " You are judging the quality of {len(models)} responses.\n", + " Evaluate each response based on:\n", + " 1. Clarity and coherence\n", + " 2. Depth of analysis\n", + " 3. Practicality of recommendations\n", + " 4. Originality of insights\n", + " \n", + " Rank the responses from best to worst.\n", + " Respond with the model index of the best response, nothing else.\n", + " \n", + " Here are the responses:\n", + " {answers}\n", + " \"\"\"\n", + " \n", + "# Get the best response\n", + "judge_response = openai.chat.completions.create(\n", + " model=\"o3-mini\",\n", + " messages=[{\"role\": \"user\", \"content\": judge_prompt}]\n", + ")\n", + "best_response = judge_response.choices[0].message.content\n", + "\n", + "print(f\"Best Response's Model: {models[int(best_response)]}\")\n", + "\n", + "synthesis_prompt = f\"\"\"\n", + " Here is the best response's model index from the judge:\n", + "\n", + " {best_response}\n", + "\n", + " And here are the responses from all the models:\n", + "\n", + " {together}\n", + "\n", + " Synthesize the responses from the non-best models into one comprehensive answer that:\n", + " 1. Captures the best insights from each response that could add value to the best response from the judge\n", + " 2. Resolves any contradictions between responses before extending the best response\n", + " 3. Presents a clear and coherent final answer that is a comprehensive extension of the best response from the judge\n", + " 4. Maintains the same format as the original best response from the judge\n", + " 5. Compiles all additional recommendations mentioned by all models\n", + "\n", + " Show the best response {answers[int(best_response)]} and then your synthesized response specifying which are additional recommendations to the best response:\n", + " \"\"\"\n", + "\n", + "# Get the synthesized response\n", + "synthesis_response = claude.messages.create(\n", + " model=\"claude-3-7-sonnet-latest\",\n", + " messages=[{\"role\": \"user\", \"content\": synthesis_prompt}],\n", + " max_tokens=10000\n", + ")\n", + "synthesized_answer = synthesis_response.content[0].text\n", + "\n", + "converted_answer = re.sub(r'\\\\[\\[\\]]', '$$', synthesized_answer)\n", + "display(Markdown(converted_answer))" + ] + } + ], + "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.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/3_lab3_groq_llama_generator_gemini_evaluator.ipynb b/community_contributions/3_lab3_groq_llama_generator_gemini_evaluator.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3c612b83cba80f33b76a0dde10a4dcc1b10f1814 --- /dev/null +++ b/community_contributions/3_lab3_groq_llama_generator_gemini_evaluator.ipynb @@ -0,0 +1,286 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Chat app with LinkedIn Profile Information - Groq LLama as Generator and Gemini as evaluator\n" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [], + "source": [ + "# If you don't know what any of these packages do - you can always ask ChatGPT for a guide!\n", + "\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "from pypdf import PdfReader\n", + "from groq import Groq\n", + "import gradio as gr" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [], + "source": [ + "load_dotenv(override=True)\n", + "groq = Groq()" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [], + "source": [ + "reader = PdfReader(\"me/My_LinkedIn.pdf\")\n", + "linkedin = \"\"\n", + "for page in reader.pages:\n", + " text = page.extract_text()\n", + " if text:\n", + " linkedin += text" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(linkedin)" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n", + " summary = f.read()" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [], + "source": [ + "name = \"Maalaiappan Subramanian\"" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n", + "particularly questions related to {name}'s career, background, skills and experience. \\\n", + "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n", + "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n", + "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n", + "If you don't know the answer, say so.\"\n", + "\n", + "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n", + "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [], + "source": [ + "def chat(message, history):\n", + " # Below line is to remove the metadata and options from the history\n", + " history = [{k: v for k, v in item.items() if k not in ('metadata', 'options')} for item in history]\n", + " messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n", + " response = groq.chat.completions.create(model=\"llama-3.3-70b-versatile\", messages=messages)\n", + " return response.choices[0].message.content" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gr.ChatInterface(chat, type=\"messages\").launch()" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a Pydantic model for the Evaluation\n", + "\n", + "from pydantic import BaseModel\n", + "\n", + "class Evaluation(BaseModel):\n", + " is_acceptable: bool\n", + " feedback: str\n" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [], + "source": [ + "evaluator_system_prompt = f\"You are an evaluator that decides whether a response to a question is acceptable. \\\n", + "You are provided with a conversation between a User and an Agent. Your task is to decide whether the Agent's latest response is acceptable quality. \\\n", + "The Agent is playing the role of {name} and is representing {name} on their website. \\\n", + "The Agent has been instructed to be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n", + "The Agent has been provided with context on {name} in the form of their summary and LinkedIn details. Here's the information:\"\n", + "\n", + "evaluator_system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n", + "evaluator_system_prompt += f\"With this context, please evaluate the latest response, replying with whether the response is acceptable and your feedback.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [], + "source": [ + "def evaluator_user_prompt(reply, message, history):\n", + " user_prompt = f\"Here's the conversation between the User and the Agent: \\n\\n{history}\\n\\n\"\n", + " user_prompt += f\"Here's the latest message from the User: \\n\\n{message}\\n\\n\"\n", + " user_prompt += f\"Here's the latest response from the Agent: \\n\\n{reply}\\n\\n\"\n", + " user_prompt += f\"Please evaluate the response, replying with whether it is acceptable and your feedback.\"\n", + " return user_prompt" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "gemini = OpenAI(\n", + " api_key=os.getenv(\"GOOGLE_API_KEY\"), \n", + " base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [], + "source": [ + "def evaluate(reply, message, history) -> Evaluation:\n", + "\n", + " messages = [{\"role\": \"system\", \"content\": evaluator_system_prompt}] + [{\"role\": \"user\", \"content\": evaluator_user_prompt(reply, message, history)}]\n", + " response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=messages, response_format=Evaluation)\n", + " return response.choices[0].message.parsed" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": {}, + "outputs": [], + "source": [ + "def rerun(reply, message, history, feedback):\n", + " # Below line is to remove the metadata and options from the history\n", + " history = [{k: v for k, v in item.items() if k not in ('metadata', 'options')} for item in history]\n", + " updated_system_prompt = system_prompt + f\"\\n\\n## Previous answer rejected\\nYou just tried to reply, but the quality control rejected your reply\\n\"\n", + " updated_system_prompt += f\"## Your attempted answer:\\n{reply}\\n\\n\"\n", + " updated_system_prompt += f\"## Reason for rejection:\\n{feedback}\\n\\n\"\n", + " messages = [{\"role\": \"system\", \"content\": updated_system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n", + " response = groq.chat.completions.create(model=\"llama-3.3-70b-versatile\", messages=messages)\n", + " return response.choices[0].message.content" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": {}, + "outputs": [], + "source": [ + "def chat(message, history):\n", + " if \"personal\" in message:\n", + " system = system_prompt + \"\\n\\nEverything in your reply needs to be in Gen Z language - \\\n", + " it is mandatory that you respond only and entirely in Gen Z language\"\n", + " else:\n", + " system = system_prompt\n", + " # Below line is to remove the metadata and options from the history\n", + " history = [{k: v for k, v in item.items() if k not in ('metadata', 'options')} for item in history]\n", + " messages = [{\"role\": \"system\", \"content\": system}] + history + [{\"role\": \"user\", \"content\": message}]\n", + " response = groq.chat.completions.create(model=\"llama-3.3-70b-versatile\", messages=messages)\n", + " reply =response.choices[0].message.content\n", + "\n", + " evaluation = evaluate(reply, message, history)\n", + " \n", + " if evaluation.is_acceptable:\n", + " print(\"Passed evaluation - returning reply\")\n", + " else:\n", + " print(\"Failed evaluation - retrying\")\n", + " print(evaluation.feedback)\n", + " reply = rerun(reply, message, history, evaluation.feedback) \n", + " return reply" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gr.ChatInterface(chat, type=\"messages\").launch()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/Business_Idea.ipynb b/community_contributions/Business_Idea.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..5df2131291186b650b6922a8474f5789622993b3 --- /dev/null +++ b/community_contributions/Business_Idea.ipynb @@ -0,0 +1,388 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Business idea generator and evaluator \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Start with imports - ask ChatGPT to explain any package that you don't know\n", + "\n", + "import os\n", + "import json\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "from anthropic import Anthropic\n", + "from IPython.display import Markdown, display" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Always remember to do this!\n", + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Print the key prefixes to help with any debugging\n", + "\n", + "openai_api_key = os.getenv('OPENAI_API_KEY')\n", + "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n", + "google_api_key = os.getenv('GOOGLE_API_KEY')\n", + "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n", + "groq_api_key = os.getenv('GROQ_API_KEY')\n", + "\n", + "if openai_api_key:\n", + " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n", + "else:\n", + " print(\"OpenAI API Key not set\")\n", + " \n", + "if anthropic_api_key:\n", + " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n", + "else:\n", + " print(\"Anthropic API Key not set (and this is optional)\")\n", + "\n", + "if google_api_key:\n", + " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n", + "else:\n", + " print(\"Google API Key not set (and this is optional)\")\n", + "\n", + "if deepseek_api_key:\n", + " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n", + "else:\n", + " print(\"DeepSeek API Key not set (and this is optional)\")\n", + "\n", + "if groq_api_key:\n", + " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n", + "else:\n", + " print(\"Groq API Key not set (and this is optional)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "request = (\n", + " \"Please generate three innovative business ideas aligned with the latest global trends. \"\n", + " \"For each idea, include a brief description (2–3 sentences).\"\n", + ")\n", + "messages = [{\"role\": \"user\", \"content\": request}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "messages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "openai = OpenAI()\n", + "'''\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages,\n", + ")\n", + "question = response.choices[0].message.content\n", + "print(question)\n", + "'''" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "competitors = []\n", + "answers = []\n", + "#messages = [{\"role\": \"user\", \"content\": question}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The API we know well\n", + "\n", + "model_name = \"gpt-4o-mini\"\n", + "\n", + "response = openai.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Anthropic has a slightly different API, and Max Tokens is required\n", + "\n", + "model_name = \"claude-3-7-sonnet-latest\"\n", + "\n", + "claude = Anthropic()\n", + "response = claude.messages.create(model=model_name, messages=messages, max_tokens=1000)\n", + "answer = response.content[0].text\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n", + "model_name = \"gemini-2.0-flash\"\n", + "\n", + "response = gemini.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n", + "model_name = \"deepseek-chat\"\n", + "\n", + "response = deepseek.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n", + "model_name = \"llama-3.3-70b-versatile\"\n", + "\n", + "response = groq.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!ollama pull llama3.2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n", + "model_name = \"llama3.2\"\n", + "\n", + "response = ollama.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# So where are we?\n", + "\n", + "print(competitors)\n", + "print(answers)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# It's nice to know how to use \"zip\"\n", + "for competitor, answer in zip(competitors, answers):\n", + " print(f\"Competitor: {competitor}\\n\\n{answer}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "# Let's bring this together - note the use of \"enumerate\"\n", + "\n", + "together = \"\"\n", + "for index, answer in enumerate(answers):\n", + " together += f\"# Response from competitor {index+1}\\n\\n\"\n", + " together += answer + \"\\n\\n\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(together)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "judge = f\"\"\"You are judging a competition between {len(competitors)} competitors.\n", + "Each model was asked to generate three innovative business ideas aligned with the latest global trends.\n", + "\n", + "Your job is to evaluate the likelihood of success for each idea on a scale from 0 to 100 percent. For each competitor, list the three percentages in the same order as their ideas.\n", + "\n", + "Respond only with JSON in this format:\n", + "{{\"results\": [\n", + " {{\"competitor\": 1, \"success_chances\": [perc1, perc2, perc3]}},\n", + " {{\"competitor\": 2, \"success_chances\": [perc1, perc2, perc3]}},\n", + " ...\n", + "]}}\n", + "\n", + "Here are the ideas from each competitor:\n", + "\n", + "{together}\n", + "\n", + "Now respond with only the JSON, nothing else.\"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(judge)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "judge_messages = [{\"role\": \"user\", \"content\": judge}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Judgement time!\n", + "\n", + "openai = OpenAI()\n", + "response = openai.chat.completions.create(\n", + " model=\"o3-mini\",\n", + " messages=judge_messages,\n", + ")\n", + "results = response.choices[0].message.content\n", + "print(results)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Parse judge results JSON and display success probabilities\n", + "results_dict = json.loads(results)\n", + "for entry in results_dict[\"results\"]:\n", + " comp_num = entry[\"competitor\"]\n", + " comp_name = competitors[comp_num - 1]\n", + " chances = entry[\"success_chances\"]\n", + " print(f\"{comp_name}:\")\n", + " for idx, perc in enumerate(chances, start=1):\n", + " print(f\" Idea {idx}: {perc}% chance of success\")\n", + " print()\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.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git "a/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/.gitignore" "b/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/.gitignore" new file mode 100644 index 0000000000000000000000000000000000000000..2eea525d885d5148108f6f3a9a8613863f783d36 --- /dev/null +++ "b/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/.gitignore" @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git "a/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/AnalyzeResume.png" "b/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/AnalyzeResume.png" new file mode 100644 index 0000000000000000000000000000000000000000..560b3edda6eb98ed2a14403df62965a54a03a9c0 Binary files /dev/null and "b/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/AnalyzeResume.png" differ diff --git "a/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/README.md" "b/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/README.md" new file mode 100644 index 0000000000000000000000000000000000000000..7357e32ba1a2cddf920bf62465db3e7c272dc29f --- /dev/null +++ "b/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/README.md" @@ -0,0 +1,48 @@ +# 🧠 Resume-Job Match Application (LLM-Powered) + +![AnalyseResume](AnalyzeResume.png) + +This is a **Streamlit-based web app** that evaluates how well a resume matches a job description using powerful Large Language Models (LLMs) such as: + +- OpenAI GPT +- Anthropic Claude +- Google Gemini (Generative AI) +- Groq LLM +- DeepSeek LLM + +The app takes a resume and job description as input files, sends them to these LLMs, and returns: + +- ✅ Match percentage from each model +- 📊 A ranked table sorted by match % +- 📈 Average match percentage +- 🧠 Simple, responsive UI for instant feedback + +## 📂 Features + +- Upload **any file type** for resume and job description (PDF, DOCX, TXT, etc.) +- Automatic extraction and cleaning of text +- Match results across multiple models in real time +- Table view with clean formatting +- Uses `.env` file for secure API key management + +## 🔐 Environment Setup (`.env`) + +Create a `.env` file in the project root and add the following API keys: + +```env +OPENAI_API_KEY=your-openai-api-key +ANTHROPIC_API_KEY=your-anthropic-api-key +GOOGLE_API_KEY=your-google-api-key +GROQ_API_KEY=your-groq-api-key +DEEPSEEK_API_KEY=your-deepseek-api-key +``` + +## ▶️ Running the App +### Launch the app using Streamlit: + +streamlit run resume_agent.py + +### The app will open in your browser at: +📍 http://localhost:8501 + + diff --git "a/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/multi_file_ingestion.py" "b/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/multi_file_ingestion.py" new file mode 100644 index 0000000000000000000000000000000000000000..a86d18388da163b2a8904dfaab9fcb8fe02abe14 --- /dev/null +++ "b/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/multi_file_ingestion.py" @@ -0,0 +1,44 @@ +import os +from langchain.document_loaders import ( + TextLoader, + PyPDFLoader, + UnstructuredWordDocumentLoader, + UnstructuredFileLoader +) + + + +def load_and_split_resume(file_path: str): + """ + Loads a resume file and splits it into text chunks using LangChain. + + Args: + file_path (str): Path to the resume file (.txt, .pdf, .docx, etc.) + chunk_size (int): Maximum characters per chunk. + chunk_overlap (int): Overlap between chunks to preserve context. + + Returns: + List[str]: List of split text chunks. + """ + if not os.path.exists(file_path): + raise FileNotFoundError(f"File not found: {file_path}") + + ext = os.path.splitext(file_path)[1].lower() + + # Select the appropriate loader + if ext == ".txt": + loader = TextLoader(file_path, encoding="utf-8") + elif ext == ".pdf": + loader = PyPDFLoader(file_path) + elif ext in [".docx", ".doc"]: + loader = UnstructuredWordDocumentLoader(file_path) + else: + # Fallback for other common formats + loader = UnstructuredFileLoader(file_path) + + # Load the file as LangChain documents + documents = loader.load() + + + return documents + # return [doc.page_content for doc in split_docs] diff --git "a/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/resume_agent.py" "b/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/resume_agent.py" new file mode 100644 index 0000000000000000000000000000000000000000..677da7aad137905dd1a88bd8c75477b9f5ef5d3e --- /dev/null +++ "b/community_contributions/Multi-Model-Resume\342\200\223JD-Match-Analyzer/resume_agent.py" @@ -0,0 +1,262 @@ +import streamlit as st +import os +from openai import OpenAI +from anthropic import Anthropic +import pdfplumber +from io import StringIO +from dotenv import load_dotenv +import pandas as pd +from multi_file_ingestion import load_and_split_resume + +# Load environment variables +load_dotenv(override=True) +openai_api_key = os.getenv("OPENAI_API_KEY") +anthropic_api_key = os.getenv("ANTHROPIC_API_KEY") +google_api_key = os.getenv("GOOGLE_API_KEY") +groq_api_key = os.getenv("GROQ_API_KEY") +deepseek_api_key = os.getenv("DEEPSEEK_API_KEY") + +openai = OpenAI() + +# Streamlit UI +st.set_page_config(page_title="LLM Resume–JD Fit", layout="wide") +st.title("🧠 Multi-Model Resume–JD Match Analyzer") + +# Inject custom CSS to reduce white space +st.markdown(""" + +""", unsafe_allow_html=True) + +# File upload +resume_file = st.file_uploader("📄 Upload Resume (any file type)", type=None) +jd_file = st.file_uploader("📝 Upload Job Description (any file type)", type=None) + +# Function to extract text from uploaded files +def extract_text(file): + if file.name.endswith(".pdf"): + with pdfplumber.open(file) as pdf: + return "\n".join([page.extract_text() for page in pdf.pages if page.extract_text()]) + else: + return StringIO(file.read().decode("utf-8")).read() + + +def extract_candidate_name(resume_text): + prompt = f""" +You are an AI assistant specialized in resume analysis. + +Your task is to get full name of the candidate from the resume. + +Resume: +{resume_text} + +Respond with only the candidate's full name. +""" + try: + response = openai.chat.completions.create( + model="gpt-4o-mini", + messages=[ + {"role": "system", "content": "You are a professional resume evaluator."}, + {"role": "user", "content": prompt} + ] + ) + content = response.choices[0].message.content + + return content.strip() + + except Exception as e: + return "Unknown" + + +# Function to build the prompt for LLMs +def build_prompt(resume_text, jd_text): + prompt = f""" +You are an AI assistant specialized in resume analysis and recruitment. Analyze the given resume and compare it with the job description. + +Your task is to evaluate how well the resume aligns with the job description. + + +Provide a match percentage between 0 and 100, where 100 indicates a perfect fit. + +Resume: +{resume_text} + +Job Description: +{jd_text} + +Respond with only the match percentage as an integer. +""" + return prompt.strip() + +# Function to get match percentage from OpenAI GPT-4 +def get_openai_match(prompt): + try: + response = openai.chat.completions.create( + model="gpt-4o-mini", + messages=[ + {"role": "system", "content": "You are a professional resume evaluator."}, + {"role": "user", "content": prompt} + ] + ) + content = response.choices[0].message.content + digits = ''.join(filter(str.isdigit, content)) + return min(int(digits), 100) if digits else 0 + except Exception as e: + st.error(f"OpenAI API Error: {e}") + return 0 + +# Function to get match percentage from Anthropic Claude +def get_anthropic_match(prompt): + try: + model_name = "claude-3-7-sonnet-latest" + claude = Anthropic() + + message = claude.messages.create( + model=model_name, + max_tokens=100, + messages=[ + {"role": "user", "content": prompt} + ] + ) + content = message.content[0].text + digits = ''.join(filter(str.isdigit, content)) + return min(int(digits), 100) if digits else 0 + except Exception as e: + st.error(f"Anthropic API Error: {e}") + return 0 + +# Function to get match percentage from Google Gemini +def get_google_match(prompt): + try: + gemini = OpenAI(api_key=google_api_key, base_url="https://generativelanguage.googleapis.com/v1beta/openai/") + model_name = "gemini-2.0-flash" + messages = [{"role": "user", "content": prompt}] + response = gemini.chat.completions.create(model=model_name, messages=messages) + content = response.choices[0].message.content + digits = ''.join(filter(str.isdigit, content)) + return min(int(digits), 100) if digits else 0 + except Exception as e: + st.error(f"Google Gemini API Error: {e}") + return 0 + +# Function to get match percentage from Groq +def get_groq_match(prompt): + try: + groq = OpenAI(api_key=groq_api_key, base_url="https://api.groq.com/openai/v1") + model_name = "llama-3.3-70b-versatile" + messages = [{"role": "user", "content": prompt}] + response = groq.chat.completions.create(model=model_name, messages=messages) + answer = response.choices[0].message.content + digits = ''.join(filter(str.isdigit, answer)) + return min(int(digits), 100) if digits else 0 + except Exception as e: + st.error(f"Groq API Error: {e}") + return 0 + +# Function to get match percentage from DeepSeek +def get_deepseek_match(prompt): + try: + deepseek = OpenAI(api_key=deepseek_api_key, base_url="https://api.deepseek.com/v1") + model_name = "deepseek-chat" + messages = [{"role": "user", "content": prompt}] + response = deepseek.chat.completions.create(model=model_name, messages=messages) + answer = response.choices[0].message.content + digits = ''.join(filter(str.isdigit, answer)) + return min(int(digits), 100) if digits else 0 + except Exception as e: + st.error(f"DeepSeek API Error: {e}") + return 0 + +# Main action +if st.button("🔍 Analyze Resume Fit"): + if resume_file and jd_file: + with st.spinner("Analyzing..."): + # resume_text = extract_text(resume_file) + # jd_text = extract_text(jd_file) + os.makedirs("temp_files", exist_ok=True) + resume_path = os.path.join("temp_files", resume_file.name) + + with open(resume_path, "wb") as f: + f.write(resume_file.getbuffer()) + resume_docs = load_and_split_resume(resume_path) + resume_text = "\n".join([doc.page_content for doc in resume_docs]) + + jd_path = os.path.join("temp_files", jd_file.name) + with open(jd_path, "wb") as f: + f.write(jd_file.getbuffer()) + jd_docs = load_and_split_resume(jd_path) + jd_text = "\n".join([doc.page_content for doc in jd_docs]) + + candidate_name = extract_candidate_name(resume_text) + prompt = build_prompt(resume_text, jd_text) + + # Get match percentages from all models + scores = { + "OpenAI GPT-4o Mini": get_openai_match(prompt), + "Anthropic Claude": get_anthropic_match(prompt), + "Google Gemini": get_google_match(prompt), + "Groq": get_groq_match(prompt), + "DeepSeek": get_deepseek_match(prompt), + } + + # Calculate average score + average_score = round(sum(scores.values()) / len(scores), 2) + + # Sort scores in descending order + sorted_scores = sorted(scores.items(), reverse=False) + + # Display results + st.success("✅ Analysis Complete") + st.subheader("📊 Match Results (Ranked by Model)") + + # Show candidate name + st.markdown(f"**👤 Candidate:** {candidate_name}") + + # Create and sort dataframe + df = pd.DataFrame(sorted_scores, columns=["Model", "% Match"]) + df = df.sort_values("% Match", ascending=False).reset_index(drop=True) + + # Convert to HTML table + def render_custom_table(dataframe): + table_html = "" + # Table header + table_html += "" + for col in dataframe.columns: + table_html += f"" + table_html += "" + + # Table rows + table_html += "" + for _, row in dataframe.iterrows(): + table_html += "" + for val in row: + table_html += f"" + table_html += "" + table_html += "
{col}
{val}
" + return table_html + + # Display table + st.markdown(render_custom_table(df), unsafe_allow_html=True) + + # Show average match + st.metric(label="📈 Average Match %", value=f"{average_score:.2f}%") + else: + st.warning("Please upload both resume and job description.") diff --git a/community_contributions/app_rate_limiter_mailgun_integration.py b/community_contributions/app_rate_limiter_mailgun_integration.py new file mode 100644 index 0000000000000000000000000000000000000000..e929d4195bfc048dd36dd7cd210b1f7957613560 --- /dev/null +++ b/community_contributions/app_rate_limiter_mailgun_integration.py @@ -0,0 +1,231 @@ +from dotenv import load_dotenv +from openai import OpenAI +import json +import os +import requests +from pypdf import PdfReader +import gradio as gr +import base64 +import time +from collections import defaultdict +import fastapi +from gradio.context import Context +import logging + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + + +load_dotenv(override=True) + +class RateLimiter: + def __init__(self, max_requests=5, time_window=5): + # max_requests per time_window seconds + self.max_requests = max_requests + self.time_window = time_window # in seconds + self.request_history = defaultdict(list) + + def is_rate_limited(self, user_id): + current_time = time.time() + # Remove old requests + self.request_history[user_id] = [ + timestamp for timestamp in self.request_history[user_id] + if current_time - timestamp < self.time_window + ] + + # Check if user has exceeded the limit + if len(self.request_history[user_id]) >= self.max_requests: + return True + + # Add current request + self.request_history[user_id].append(current_time) + return False + +def push(text): + requests.post( + "https://api.pushover.net/1/messages.json", + data={ + "token": os.getenv("PUSHOVER_TOKEN"), + "user": os.getenv("PUSHOVER_USER"), + "message": text, + } + ) + +def send_email(from_email, name, notes): + auth = base64.b64encode(f'api:{os.getenv("MAILGUN_API_KEY")}'.encode()).decode() + + response = requests.post( + f'https://api.mailgun.net/v3/{os.getenv("MAILGUN_DOMAIN")}/messages', + headers={ + 'Authorization': f'Basic {auth}' + }, + data={ + 'from': f'Website Contact ', + 'to': os.getenv("MAILGUN_RECIPIENT"), + 'subject': f'New message from {from_email}', + 'text': f'Name: {name}\nEmail: {from_email}\nNotes: {notes}', + 'h:Reply-To': from_email + } + ) + + return response.status_code == 200 + + +def record_user_details(email, name="Name not provided", notes="not provided"): + push(f"Recording {name} with email {email} and notes {notes}") + # Send email notification + email_sent = send_email(email, name, notes) + return {"recorded": "ok", "email_sent": email_sent} + +def record_unknown_question(question): + push(f"Recording {question}") + return {"recorded": "ok"} + +record_user_details_json = { + "name": "record_user_details", + "description": "Use this tool to record that a user is interested in being in touch and provided an email address", + "parameters": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "The email address of this user" + }, + "name": { + "type": "string", + "description": "The user's name, if they provided it" + } + , + "notes": { + "type": "string", + "description": "Any additional information about the conversation that's worth recording to give context" + } + }, + "required": ["email"], + "additionalProperties": False + } +} + +record_unknown_question_json = { + "name": "record_unknown_question", + "description": "Always use this tool to record any question that couldn't be answered as you didn't know the answer", + "parameters": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "The question that couldn't be answered" + }, + }, + "required": ["question"], + "additionalProperties": False + } +} + +tools = [{"type": "function", "function": record_user_details_json}, + {"type": "function", "function": record_unknown_question_json}] + + +class Me: + + def __init__(self): + self.openai = OpenAI(api_key=os.getenv("GOOGLE_API_KEY"), base_url="https://generativelanguage.googleapis.com/v1beta/openai/") + self.name = "Sagarnil Das" + self.rate_limiter = RateLimiter(max_requests=5, time_window=60) # 5 messages per minute + reader = PdfReader("me/linkedin.pdf") + self.linkedin = "" + for page in reader.pages: + text = page.extract_text() + if text: + self.linkedin += text + with open("me/summary.txt", "r", encoding="utf-8") as f: + self.summary = f.read() + + + def handle_tool_call(self, tool_calls): + results = [] + for tool_call in tool_calls: + tool_name = tool_call.function.name + arguments = json.loads(tool_call.function.arguments) + print(f"Tool called: {tool_name}", flush=True) + tool = globals().get(tool_name) + result = tool(**arguments) if tool else {} + results.append({"role": "tool","content": json.dumps(result),"tool_call_id": tool_call.id}) + return results + + def system_prompt(self): + system_prompt = f"You are acting as {self.name}. You are answering questions on {self.name}'s website, \ +particularly questions related to {self.name}'s career, background, skills and experience. \ +Your responsibility is to represent {self.name} for interactions on the website as faithfully as possible. \ +You are given a summary of {self.name}'s background and LinkedIn profile which you can use to answer questions. \ +Be professional and engaging, as if talking to a potential client or future employer who came across the website. \ +If you don't know the answer to any question, use your record_unknown_question tool to record the question that you couldn't answer, even if it's about something trivial or unrelated to career. \ +If the user is engaging in discussion, try to steer them towards getting in touch via email; ask for their email and record it using your record_user_details tool. \ +When a user provides their email, both a push notification and an email notification will be sent. If the user does not provide any note in the message \ +in which they provide their email, then give a summary of the conversation so far as the notes." + + system_prompt += f"\n\n## Summary:\n{self.summary}\n\n## LinkedIn Profile:\n{self.linkedin}\n\n" + system_prompt += f"With this context, please chat with the user, always staying in character as {self.name}." + return system_prompt + + def chat(self, message, history): + # Get the client IP from Gradio's request context + try: + # Try to get the real client IP from request headers + request = Context.get_context().request + # Check for X-Forwarded-For header (common in reverse proxies like HF Spaces) + forwarded_for = request.headers.get("X-Forwarded-For") + # Check for Cf-Connecting-IP header (Cloudflare) + cloudflare_ip = request.headers.get("Cf-Connecting-IP") + + if forwarded_for: + # X-Forwarded-For contains a comma-separated list of IPs, the first one is the client + user_id = forwarded_for.split(",")[0].strip() + elif cloudflare_ip: + user_id = cloudflare_ip + else: + # Fall back to direct client address + user_id = request.client.host + except (AttributeError, RuntimeError, fastapi.exceptions.FastAPIError): + # Fallback if we can't get context or if running outside of FastAPI + user_id = "default_user" + logger.debug(f"User ID: {user_id}") + if self.rate_limiter.is_rate_limited(user_id): + return "You're sending messages too quickly. Please wait a moment before sending another message." + + messages = [{"role": "system", "content": self.system_prompt()}] + + # Check if history is a list of dicts (Gradio "messages" format) + if isinstance(history, list) and all(isinstance(h, dict) for h in history): + messages.extend(history) + else: + # Assume it's a list of [user_msg, assistant_msg] pairs + for user_msg, assistant_msg in history: + messages.append({"role": "user", "content": user_msg}) + messages.append({"role": "assistant", "content": assistant_msg}) + + messages.append({"role": "user", "content": message}) + + done = False + while not done: + response = self.openai.chat.completions.create( + model="gemini-2.0-flash", + messages=messages, + tools=tools + ) + if response.choices[0].finish_reason == "tool_calls": + tool_calls = response.choices[0].message.tool_calls + tool_result = self.handle_tool_call(tool_calls) + messages.append(response.choices[0].message) + messages.extend(tool_result) + else: + done = True + + return response.choices[0].message.content + + + +if __name__ == "__main__": + me = Me() + gr.ChatInterface(me.chat, type="messages").launch() + \ No newline at end of file diff --git a/community_contributions/community.ipynb b/community_contributions/community.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..59f492d1d7eb7e6a21d7a6c5a523f5230765cf67 --- /dev/null +++ b/community_contributions/community.ipynb @@ -0,0 +1,29 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Community contributions\n", + "\n", + "Thank you for considering contributing your work to the repo!\n", + "\n", + "Please add your code (modules or notebooks) to this directory and send me a PR, per the instructions in the guides.\n", + "\n", + "I'd love to share your progress with other students, so everyone can benefit from your projects.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/ecrg_3_lab3.ipynb b/community_contributions/ecrg_3_lab3.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..4c275bbd3708244471d061e6e99296709975648b --- /dev/null +++ b/community_contributions/ecrg_3_lab3.ipynb @@ -0,0 +1,514 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Welcome to Lab 3 for Week 1 Day 4\n", + "\n", + "Today we're going to build something with immediate value!\n", + "\n", + "In the folder `me` I've put a single file `linkedin.pdf` - it's a PDF download of my LinkedIn profile.\n", + "\n", + "Please replace it with yours!\n", + "\n", + "I've also made a file called `summary.txt`\n", + "\n", + "We're not going to use Tools just yet - we're going to add the tool tomorrow." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Import necessary libraries:\n", + "# - load_dotenv: Loads environment variables from a .env file (e.g., your OpenAI API key).\n", + "# - OpenAI: The official OpenAI client to interact with their API.\n", + "# - PdfReader: Used to read and extract text from PDF files.\n", + "# - gr: Gradio is a UI library to quickly build web interfaces for machine learning apps.\n", + "\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "from pypdf import PdfReader\n", + "import gradio as gr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "load_dotenv(override=True)\n", + "openai = OpenAI()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This script reads a PDF file located at 'me/profile.pdf' and extracts all the text from each page.\n", + "The extracted text is concatenated into a single string variable named 'linkedin'.\n", + "This can be useful for feeding structured content (like a resume or profile) into an AI model or for further text processing.\n", + "\"\"\"\n", + "reader = PdfReader(\"me/profile.pdf\")\n", + "linkedin = \"\"\n", + "for page in reader.pages:\n", + " text = page.extract_text()\n", + " if text:\n", + " linkedin += text" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This script loads a PDF file named 'projects.pdf' from the 'me' directory\n", + "and extracts text from each page. The extracted text is combined into a single\n", + "string variable called 'projects', which can be used later for analysis,\n", + "summarization, or input into an AI model.\n", + "\"\"\"\n", + "\n", + "reader = PdfReader(\"me/projects.pdf\")\n", + "projects = \"\"\n", + "for page in reader.pages:\n", + " text = page.extract_text()\n", + " if text:\n", + " projects += text" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Print for sanity checks\n", + "\"Print for sanity checks\"\n", + "\n", + "print(linkedin)\n", + "print(projects)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n", + " summary = f.read()\n", + "\n", + "name = \"Cristina Rodriguez\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This code constructs a system prompt for an AI agent to role-play as a specific person (defined by `name`).\n", + "The prompt guides the AI to answer questions as if it were that person, using their career summary,\n", + "LinkedIn profile, and project information for context. The final prompt ensures that the AI stays\n", + "in character and responds professionally and helpfully to visitors on the user's website.\n", + "\"\"\"\n", + "\n", + "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n", + "particularly questions related to {name}'s career, background, skills and experience. \\\n", + "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n", + "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n", + "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n", + "If you don't know the answer, say so.\"\n", + "\n", + "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\\n\\n## Projects:\\n{projects}\\n\\n\"\n", + "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This function handles a chat interaction with the OpenAI API.\n", + "\n", + "It takes the user's latest message and conversation history,\n", + "prepends a system prompt to define the AI's role and context,\n", + "and sends the full message list to the GPT-4o-mini model.\n", + "\n", + "The function returns the AI's response text from the API's output.\n", + "\"\"\"\n", + "\n", + "def chat(message, history):\n", + " messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n", + " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n", + " return response.choices[0].message.content" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This line launches a Gradio chat interface using the `chat` function to handle user input.\n", + "\n", + "- `gr.ChatInterface(chat, type=\"messages\")` creates a UI that supports message-style chat interactions.\n", + "- `launch(share=True)` starts the web app and generates a public shareable link so others can access it.\n", + "\"\"\"\n", + "\n", + "gr.ChatInterface(chat, type=\"messages\").launch(share=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A lot is about to happen...\n", + "\n", + "1. Be able to ask an LLM to evaluate an answer\n", + "2. Be able to rerun if the answer fails evaluation\n", + "3. Put this together into 1 workflow\n", + "\n", + "All without any Agentic framework!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This code defines a Pydantic model named 'Evaluation' to structure evaluation data.\n", + "\n", + "The model includes:\n", + "- is_acceptable (bool): Indicates whether the submission meets the criteria.\n", + "- feedback (str): Provides written feedback or suggestions for improvement.\n", + "\n", + "Pydantic ensures type validation and data consistency.\n", + "\"\"\"\n", + "\n", + "from pydantic import BaseModel\n", + "\n", + "class Evaluation(BaseModel):\n", + " is_acceptable: bool\n", + " feedback: str\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This code builds a system prompt for an AI evaluator agent.\n", + "\n", + "The evaluator's role is to assess the quality of an Agent's response in a simulated conversation,\n", + "where the Agent is acting as {name} on their personal/professional website.\n", + "\n", + "The evaluator receives context including {name}'s summary and LinkedIn profile,\n", + "and is instructed to determine whether the Agent's latest reply is acceptable,\n", + "while providing constructive feedback.\n", + "\"\"\"\n", + "\n", + "evaluator_system_prompt = f\"You are an evaluator that decides whether a response to a question is acceptable. \\\n", + "You are provided with a conversation between a User and an Agent. Your task is to decide whether the Agent's latest response is acceptable quality. \\\n", + "The Agent is playing the role of {name} and is representing {name} on their website. \\\n", + "The Agent has been instructed to be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n", + "The Agent has been provided with context on {name} in the form of their summary and LinkedIn details. Here's the information:\"\n", + "\n", + "evaluator_system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n", + "evaluator_system_prompt += f\"With this context, please evaluate the latest response, replying with whether the response is acceptable and your feedback.\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This function generates a user prompt for the evaluator agent.\n", + "\n", + "It organizes the full conversation context by including:\n", + "- the full chat history,\n", + "- the most recent user message,\n", + "- and the most recent agent reply.\n", + "\n", + "The final prompt instructs the evaluator to assess the quality of the agent’s response,\n", + "and return both an acceptability judgment and constructive feedback.\n", + "\"\"\"\n", + "\n", + "def evaluator_user_prompt(reply, message, history):\n", + " user_prompt = f\"Here's the conversation between the User and the Agent: \\n\\n{history}\\n\\n\"\n", + " user_prompt += f\"Here's the latest message from the User: \\n\\n{message}\\n\\n\"\n", + " user_prompt += f\"Here's the latest response from the Agent: \\n\\n{reply}\\n\\n\"\n", + " user_prompt += f\"Please evaluate the response, replying with whether it is acceptable and your feedback.\"\n", + " return user_prompt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This script tests whether the Google Generative AI API key is working correctly.\n", + "\n", + "- It loads the API key from a .env file using `dotenv`.\n", + "- Initializes a genai.Client with the loaded key.\n", + "- Attempts to generate a simple response using the \"gemini-2.0-flash\" model.\n", + "- Prints confirmation if the key is valid, or shows an error message if the request fails.\n", + "\"\"\"\n", + "\n", + "from dotenv import load_dotenv\n", + "import os\n", + "from google import genai\n", + "\n", + "load_dotenv()\n", + "\n", + "client = genai.Client(api_key=os.environ.get(\"GOOGLE_API_KEY\"))\n", + "\n", + "try:\n", + " # Use the correct method for genai.Client\n", + " test_response = client.models.generate_content(\n", + " model=\"gemini-2.0-flash\",\n", + " contents=\"Hello\"\n", + " )\n", + " print(\"✅ API key is working!\")\n", + " print(f\"Response: {test_response.text}\")\n", + "except Exception as e:\n", + " print(f\"❌ API key test failed: {e}\")\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This line initializes an OpenAI-compatible client for accessing Google's Generative Language API.\n", + "\n", + "- `api_key` is retrieved from environment variables.\n", + "- `base_url` points to Google's OpenAI-compatible endpoint.\n", + "\n", + "This setup allows you to use OpenAI-style syntax to interact with Google's Gemini models.\n", + "\"\"\"\n", + "\n", + "gemini = OpenAI(\n", + " api_key=os.environ.get(\"GOOGLE_API_KEY\"),\n", + " base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This function sends a structured evaluation request to the Gemini API and returns a parsed `Evaluation` object.\n", + "\n", + "- It constructs the message list using:\n", + " - a system prompt defining the evaluator's role and context\n", + " - a user prompt containing the conversation history, user message, and agent reply\n", + "\n", + "- It uses Gemini's OpenAI-compatible API to process the evaluation request,\n", + " specifying `response_format=Evaluation` to get a structured response.\n", + "\n", + "- The function returns the parsed evaluation result (acceptability and feedback).\n", + "\"\"\"\n", + "\n", + "def evaluate(reply, message, history) -> Evaluation:\n", + "\n", + " messages = [{\"role\": \"system\", \"content\": evaluator_system_prompt}] + [{\"role\": \"user\", \"content\": evaluator_user_prompt(reply, message, history)}]\n", + " response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=messages, response_format=Evaluation)\n", + " return response.choices[0].message.parsed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This code sends a test question to the AI agent and evaluates its response.\n", + "\n", + "1. It builds a message list including:\n", + " - the system prompt that defines the agent’s role\n", + " - a user question: \"do you hold a patent?\"\n", + "\n", + "2. The message list is sent to OpenAI's GPT-4o-mini model to generate a response.\n", + "\n", + "3. The reply is extracted from the API response.\n", + "\n", + "4. The `evaluate()` function is then called with:\n", + " - the agent’s reply\n", + " - the original user message\n", + " - and just the system prompt as history (no prior user/agent exchange)\n", + "\n", + "This allows automated evaluation of how well the agent answers the question.\n", + "\"\"\"\n", + "\n", + "messages = [{\"role\": \"system\", \"content\": system_prompt}] + [{\"role\": \"user\", \"content\": \"do you hold a patent?\"}]\n", + "response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n", + "reply = response.choices[0].message.content\n", + "reply\n", + "evaluate(reply, \"do you hold a patent?\", messages[:1])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This function re-generates a response after a previous reply was rejected during evaluation.\n", + "\n", + "It:\n", + "1. Appends rejection feedback to the original system prompt to inform the agent of:\n", + " - its previous answer,\n", + " - and the reason it was rejected.\n", + "\n", + "2. Reconstructs the full message list including:\n", + " - the updated system prompt,\n", + " - the prior conversation history,\n", + " - and the original user message.\n", + "\n", + "3. Sends the updated prompt to OpenAI's GPT-4o-mini model.\n", + "\n", + "4. Returns a revised response from the model that ideally addresses the feedback.\n", + "\"\"\"\n", + "def rerun(reply, message, history, feedback):\n", + " updated_system_prompt = system_prompt + f\"\\n\\n## Previous answer rejected\\nYou just tried to reply, but the quality control rejected your reply\\n\"\n", + " updated_system_prompt += f\"## Your attempted answer:\\n{reply}\\n\\n\"\n", + " updated_system_prompt += f\"## Reason for rejection:\\n{feedback}\\n\\n\"\n", + " messages = [{\"role\": \"system\", \"content\": updated_system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n", + " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n", + " return response.choices[0].message.content" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "This function handles a chat interaction with conditional behavior and automatic quality control.\n", + "\n", + "Steps:\n", + "1. If the user's message contains the word \"patent\", the agent is instructed to respond entirely in Pig Latin by appending an instruction to the system prompt.\n", + "2. Constructs the full message history including the updated system prompt, prior conversation, and the new user message.\n", + "3. Sends the request to OpenAI's GPT-4o-mini model and receives a reply.\n", + "4. Evaluates the reply using a separate evaluator agent to determine if the response meets quality standards.\n", + "5. If the evaluation passes, the reply is returned.\n", + "6. If the evaluation fails, the function logs the feedback and calls `rerun()` to generate a corrected reply based on the feedback.\n", + "\"\"\"\n", + "\n", + "def chat(message, history):\n", + " if \"patent\" in message:\n", + " system = system_prompt + \"\\n\\nEverything in your reply needs to be in pig latin - \\\n", + " it is mandatory that you respond only and entirely in pig latin\"\n", + " else:\n", + " system = system_prompt\n", + " messages = [{\"role\": \"system\", \"content\": system}] + history + [{\"role\": \"user\", \"content\": message}]\n", + " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n", + " reply =response.choices[0].message.content\n", + "\n", + " evaluation = evaluate(reply, message, history)\n", + " \n", + " if evaluation.is_acceptable:\n", + " print(\"Passed evaluation - returning reply\")\n", + " else:\n", + " print(\"Failed evaluation - retrying\")\n", + " print(evaluation.feedback)\n", + " reply = rerun(reply, message, history, evaluation.feedback) \n", + " return reply" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\nThis launches a Gradio chat interface using the `chat` function.\\n\\n- `type=\"messages\"` enables multi-turn chat with message bubbles.\\n- `share=True` generates a public link so others can interact with the app.\\n'" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\"\"\n", + "This launches a Gradio chat interface using the `chat` function.\n", + "\n", + "- `type=\"messages\"` enables multi-turn chat with message bubbles.\n", + "- `share=True` generates a public link so others can interact with the app.\n", + "\"\"\"\n", + "gr.ChatInterface(chat, type=\"messages\").launch(share=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/gemini_based_chatbot/.env.example b/community_contributions/gemini_based_chatbot/.env.example new file mode 100644 index 0000000000000000000000000000000000000000..6109d95dd3b8c541ddb125ab659d9ade5563def2 --- /dev/null +++ b/community_contributions/gemini_based_chatbot/.env.example @@ -0,0 +1 @@ +GOOGLE_API_KEY="YOUR_API_KEY" \ No newline at end of file diff --git a/community_contributions/gemini_based_chatbot/.gitignore b/community_contributions/gemini_based_chatbot/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..860b4a9c169ff1eeac05c0cba8c744808d48098c --- /dev/null +++ b/community_contributions/gemini_based_chatbot/.gitignore @@ -0,0 +1,32 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Virtual environment +venv/ +env/ +.venv/ + +# Jupyter notebook checkpoints +.ipynb_checkpoints/ + +# Environment variable files +.env + +# Mac/OSX system files +.DS_Store + +# PyCharm/VSCode config +.idea/ +.vscode/ + +# PDFs and summaries +# Profile.pdf +# summary.txt + +# Node modules (if any) +node_modules/ + +# Other temporary files +*.log diff --git a/community_contributions/gemini_based_chatbot/Profile.pdf b/community_contributions/gemini_based_chatbot/Profile.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cf2543410412983dcb389d93ee6b1b6c0dd8ab56 Binary files /dev/null and b/community_contributions/gemini_based_chatbot/Profile.pdf differ diff --git a/community_contributions/gemini_based_chatbot/README.md b/community_contributions/gemini_based_chatbot/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8e42ef420d246e876bd661f8c9ec2093837feb46 --- /dev/null +++ b/community_contributions/gemini_based_chatbot/README.md @@ -0,0 +1,74 @@ + +# Gemini Chatbot of Users (Me) + +A simple AI chatbot that represents **Rishabh Dubey** by leveraging Google Gemini API, Gradio for UI, and context from **summary.txt** and **Profile.pdf**. + +## Screenshots +![image](https://github.com/user-attachments/assets/c6d417df-aa6a-482e-9289-eeb8e9e0f3d2) + + +## Features +- Loads background and profile data to answer questions in character. +- Uses Google Gemini for natural language responses. +- Runs in Gradio interface for easy web deployment. + +## Requirements +- Python 3.10+ +- API key for Google Gemini stored in `.env` file as `GOOGLE_API_KEY`. + +## Installation + +1. Clone this repo: + + ```bash + https://github.com/rishabh3562/Agentic-chatbot-me.git + ``` + +2. Create a virtual environment: + + ```bash + python -m venv venv + source venv/bin/activate # On Windows: venv\Scripts\activate + ``` + +3. Install dependencies: + + ```bash + pip install -r requirements.txt + ``` + +4. Add your API key in a `.env` file: + + ``` + GOOGLE_API_KEY= + ``` + + +## Usage + +Run locally: + +```bash +python app.py +``` + +The app will launch a Gradio interface at `http://127.0.0.1:7860`. + +## Deployment + +This app can be deployed on: + +* **Render** or **Hugging Face Spaces** + Make sure `.env` and static files (`summary.txt`, `Profile.pdf`) are included. + +--- + +**Note:** + +* Make sure you have `summary.txt` and `Profile.pdf` in the root directory. +* Update `requirements.txt` with `python-dotenv` if not already present. + +--- + + + diff --git a/community_contributions/gemini_based_chatbot/app.py b/community_contributions/gemini_based_chatbot/app.py new file mode 100644 index 0000000000000000000000000000000000000000..5109cd29cf53d141d24445fba842a7b3abdcc80d --- /dev/null +++ b/community_contributions/gemini_based_chatbot/app.py @@ -0,0 +1,58 @@ +import os +import google.generativeai as genai +from google.generativeai import GenerativeModel +import gradio as gr +from dotenv import load_dotenv +from PyPDF2 import PdfReader + +# Load environment variables +load_dotenv() +api_key = os.environ.get('GOOGLE_API_KEY') + +# Configure Gemini +genai.configure(api_key=api_key) +model = GenerativeModel("gemini-1.5-flash") + +# Load profile data +with open("summary.txt", "r", encoding="utf-8") as f: + summary = f.read() + +reader = PdfReader("Profile.pdf") +linkedin = "" +for page in reader.pages: + text = page.extract_text() + if text: + linkedin += text + +# System prompt +name = "Rishabh Dubey" +system_prompt = f""" +You are acting as {name}. You are answering questions on {name}'s website, +particularly questions related to {name}'s career, background, skills and experience. +Your responsibility is to represent {name} for interactions on the website as faithfully as possible. +You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. +Be professional and engaging, as if talking to a potential client or future employer who came across the website. +If you don't know the answer, say so. + +## Summary: +{summary} + +## LinkedIn Profile: +{linkedin} + +With this context, please chat with the user, always staying in character as {name}. +""" + +def chat(message, history): + conversation = f"System: {system_prompt}\n" + for user_msg, bot_msg in history: + conversation += f"User: {user_msg}\nAssistant: {bot_msg}\n" + conversation += f"User: {message}\nAssistant:" + + response = model.generate_content([conversation]) + return response.text + +if __name__ == "__main__": + # Make sure to bind to the port Render sets (default: 10000) for Render deployment + port = int(os.environ.get("PORT", 10000)) + gr.ChatInterface(chat, chatbot=gr.Chatbot()).launch(server_name="0.0.0.0", server_port=port) diff --git a/community_contributions/gemini_based_chatbot/gemini_chatbot_of_me.ipynb b/community_contributions/gemini_based_chatbot/gemini_chatbot_of_me.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..8ba32f685a1ef82248734889d4b19d08f7cf3be5 --- /dev/null +++ b/community_contributions/gemini_based_chatbot/gemini_chatbot_of_me.ipynb @@ -0,0 +1,541 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 25, + "id": "ae0bec14", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: google-generativeai in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (0.8.4)\n", + "Requirement already satisfied: OpenAI in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (1.82.0)\n", + "Requirement already satisfied: pypdf in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (5.5.0)\n", + "Requirement already satisfied: gradio in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (5.31.0)\n", + "Requirement already satisfied: PyPDF2 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (3.0.1)\n", + "Requirement already satisfied: markdown in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (3.8)\n", + "Requirement already satisfied: google-ai-generativelanguage==0.6.15 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-generativeai) (0.6.15)\n", + "Requirement already satisfied: google-api-core in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-generativeai) (2.24.1)\n", + "Requirement already satisfied: google-api-python-client in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-generativeai) (2.162.0)\n", + "Requirement already satisfied: google-auth>=2.15.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-generativeai) (2.38.0)\n", + "Requirement already satisfied: protobuf in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-generativeai) (5.29.3)\n", + "Requirement already satisfied: pydantic in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-generativeai) (2.10.6)\n", + "Requirement already satisfied: tqdm in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-generativeai) (4.67.1)\n", + "Requirement already satisfied: typing-extensions in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-generativeai) (4.12.2)\n", + "Requirement already satisfied: proto-plus<2.0.0dev,>=1.22.3 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-ai-generativelanguage==0.6.15->google-generativeai) (1.26.0)\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from OpenAI) (4.2.0)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from OpenAI) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from OpenAI) (0.28.1)\n", + "Requirement already satisfied: jiter<1,>=0.4.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from OpenAI) (0.10.0)\n", + "Requirement already satisfied: sniffio in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from OpenAI) (1.3.0)\n", + "Requirement already satisfied: aiofiles<25.0,>=22.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (24.1.0)\n", + "Requirement already satisfied: fastapi<1.0,>=0.115.2 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.115.12)\n", + "Requirement already satisfied: ffmpy in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.5.0)\n", + "Requirement already satisfied: gradio-client==1.10.1 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (1.10.1)\n", + "Requirement already satisfied: groovy~=0.1 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.1.2)\n", + "Requirement already satisfied: huggingface-hub>=0.28.1 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.32.0)\n", + "Requirement already satisfied: jinja2<4.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (3.1.6)\n", + "Requirement already satisfied: markupsafe<4.0,>=2.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (2.1.3)\n", + "Requirement already satisfied: numpy<3.0,>=1.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (1.26.4)\n", + "Requirement already satisfied: orjson~=3.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (3.10.18)\n", + "Requirement already satisfied: packaging in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (23.2)\n", + "Requirement already satisfied: pandas<3.0,>=1.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (2.1.4)\n", + "Requirement already satisfied: pillow<12.0,>=8.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (10.2.0)\n", + "Requirement already satisfied: pydub in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.25.1)\n", + "Requirement already satisfied: python-multipart>=0.0.18 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.0.20)\n", + "Requirement already satisfied: pyyaml<7.0,>=5.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (6.0.1)\n", + "Requirement already satisfied: ruff>=0.9.3 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.11.11)\n", + "Requirement already satisfied: safehttpx<0.2.0,>=0.1.6 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.1.6)\n", + "Requirement already satisfied: semantic-version~=2.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (2.10.0)\n", + "Requirement already satisfied: starlette<1.0,>=0.40.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.46.2)\n", + "Requirement already satisfied: tomlkit<0.14.0,>=0.12.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.13.2)\n", + "Requirement already satisfied: typer<1.0,>=0.12 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.15.3)\n", + "Requirement already satisfied: uvicorn>=0.14.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio) (0.34.2)\n", + "Requirement already satisfied: fsspec in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio-client==1.10.1->gradio) (2025.5.0)\n", + "Requirement already satisfied: websockets<16.0,>=10.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from gradio-client==1.10.1->gradio) (15.0.1)\n", + "Requirement already satisfied: idna>=2.8 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from anyio<5,>=3.5.0->OpenAI) (3.6)\n", + "Requirement already satisfied: googleapis-common-protos<2.0.dev0,>=1.56.2 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-api-core->google-generativeai) (1.68.0)\n", + "Requirement already satisfied: requests<3.0.0.dev0,>=2.18.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-api-core->google-generativeai) (2.31.0)\n", + "Requirement already satisfied: cachetools<6.0,>=2.0.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-auth>=2.15.0->google-generativeai) (5.5.2)\n", + "Requirement already satisfied: pyasn1-modules>=0.2.1 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-auth>=2.15.0->google-generativeai) (0.4.1)\n", + "Requirement already satisfied: rsa<5,>=3.1.4 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-auth>=2.15.0->google-generativeai) (4.9)\n", + "Requirement already satisfied: certifi in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from httpx<1,>=0.23.0->OpenAI) (2023.11.17)\n", + "Requirement already satisfied: httpcore==1.* in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from httpx<1,>=0.23.0->OpenAI) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from httpcore==1.*->httpx<1,>=0.23.0->OpenAI) (0.16.0)\n", + "Requirement already satisfied: filelock in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from huggingface-hub>=0.28.1->gradio) (3.17.0)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from pandas<3.0,>=1.0->gradio) (2.8.2)\n", + "Requirement already satisfied: pytz>=2020.1 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from pandas<3.0,>=1.0->gradio) (2023.3.post1)\n", + "Requirement already satisfied: tzdata>=2022.1 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from pandas<3.0,>=1.0->gradio) (2023.4)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from pydantic->google-generativeai) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.27.2 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from pydantic->google-generativeai) (2.27.2)\n", + "Requirement already satisfied: colorama in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from tqdm->google-generativeai) (0.4.6)\n", + "Requirement already satisfied: click>=8.0.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from typer<1.0,>=0.12->gradio) (8.1.8)\n", + "Requirement already satisfied: shellingham>=1.3.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from typer<1.0,>=0.12->gradio) (1.5.4)\n", + "Requirement already satisfied: rich>=10.11.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from typer<1.0,>=0.12->gradio) (14.0.0)\n", + "Requirement already satisfied: httplib2<1.dev0,>=0.19.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-api-python-client->google-generativeai) (0.22.0)\n", + "Requirement already satisfied: google-auth-httplib2<1.0.0,>=0.2.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-api-python-client->google-generativeai) (0.2.0)\n", + "Requirement already satisfied: uritemplate<5,>=3.0.1 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-api-python-client->google-generativeai) (4.1.1)\n", + "Requirement already satisfied: grpcio<2.0dev,>=1.33.2 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-api-core[grpc]!=2.0.*,!=2.1.*,!=2.10.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,<3.0.0dev,>=1.34.1->google-ai-generativelanguage==0.6.15->google-generativeai) (1.71.0rc2)\n", + "Requirement already satisfied: grpcio-status<2.0.dev0,>=1.33.2 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from google-api-core[grpc]!=2.0.*,!=2.1.*,!=2.10.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,<3.0.0dev,>=1.34.1->google-ai-generativelanguage==0.6.15->google-generativeai) (1.71.0rc2)\n", + "Requirement already satisfied: pyparsing!=3.0.0,!=3.0.1,!=3.0.2,!=3.0.3,<4,>=2.4.2 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from httplib2<1.dev0,>=0.19.0->google-api-python-client->google-generativeai) (3.1.1)\n", + "Requirement already satisfied: pyasn1<0.7.0,>=0.4.6 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from pyasn1-modules>=0.2.1->google-auth>=2.15.0->google-generativeai) (0.6.1)\n", + "Requirement already satisfied: six>=1.5 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from python-dateutil>=2.8.2->pandas<3.0,>=1.0->gradio) (1.16.0)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from requests<3.0.0.dev0,>=2.18.0->google-api-core->google-generativeai) (3.3.2)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from requests<3.0.0.dev0,>=2.18.0->google-api-core->google-generativeai) (2.1.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from rich>=10.11.0->typer<1.0,>=0.12->gradio) (3.0.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from rich>=10.11.0->typer<1.0,>=0.12->gradio) (2.17.2)\n", + "Requirement already satisfied: mdurl~=0.1 in c:\\users\\risha\\appdata\\local\\programs\\python\\python312\\lib\\site-packages (from markdown-it-py>=2.2.0->rich>=10.11.0->typer<1.0,>=0.12->gradio) (0.1.2)\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "[notice] A new release of pip is available: 25.0 -> 25.1.1\n", + "[notice] To update, run: python.exe -m pip install --upgrade pip\n" + ] + } + ], + "source": [ + "%pip install google-generativeai OpenAI pypdf gradio PyPDF2 markdown" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "fd2098ed", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import google.generativeai as genai\n", + "from google.generativeai import GenerativeModel\n", + "from pypdf import PdfReader\n", + "import gradio as gr\n", + "from dotenv import load_dotenv\n", + "from markdown import markdown\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "6464f7d9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "api_key loaded , starting with: AIz\n" + ] + } + ], + "source": [ + "load_dotenv(override=True)\n", + "api_key=os.environ['GOOGLE_API_KEY']\n", + "print(f\"api_key loaded , starting with: {api_key[:3]}\")\n", + "\n", + "genai.configure(api_key=api_key)\n", + "model = GenerativeModel(\"gemini-1.5-flash\")" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "b0541a87", + "metadata": {}, + "outputs": [], + "source": [ + "from bs4 import BeautifulSoup\n", + "\n", + "def prettify_gemini_response(response):\n", + " # Parse HTML\n", + " soup = BeautifulSoup(response, \"html.parser\")\n", + " # Extract plain text\n", + " plain_text = soup.get_text(separator=\"\\n\")\n", + " # Clean up extra newlines\n", + " pretty_text = \"\\n\".join([line.strip() for line in plain_text.split(\"\\n\") if line.strip()])\n", + " return pretty_text\n", + "\n", + "# Usage\n", + "# pretty_response = prettify_gemini_response(response.text)\n", + "# display(pretty_response)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9fa00c43", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "b303e991", + "metadata": {}, + "outputs": [], + "source": [ + "from PyPDF2 import PdfReader\n", + "\n", + "reader = PdfReader(\"Profile.pdf\")\n", + "\n", + "linkedin = \"\"\n", + "for page in reader.pages:\n", + " text = page.extract_text()\n", + " if text:\n", + " linkedin += text\n" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "587af4d6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "   \n", + "Contact\n", + "dubeyrishabh108@gmail.com\n", + "www.linkedin.com/in/rishabh108\n", + "(LinkedIn)\n", + "read.cv/rishabh108 (Other)\n", + "github.com/rishabh3562 (Other)\n", + "Top Skills\n", + "Big Data\n", + "CRISP-DM\n", + "Data Science\n", + "Languages\n", + "English (Professional Working)\n", + "Hindi (Native or Bilingual)\n", + "Certifications\n", + "Data Science Methodology\n", + "Create and Manage Cloud\n", + "Resources\n", + "Python Project for Data Science\n", + "Level 3: GenAI\n", + "Perform Foundational Data, ML, and\n", + "AI Tasks in Google CloudRishabh Dubey\n", + "Full Stack Developer | Freelancer | App Developer\n", + "Greater Jabalpur Area\n", + "Summary\n", + "Hi! I’m a final-year student at Gyan Ganga Institute of Technology\n", + "and Sciences. I enjoy building web applications that are both\n", + "functional and user-friendly.\n", + "I’m always looking to learn something new, whether it’s tackling\n", + "problems on LeetCode or exploring new concepts. I prefer keeping\n", + "things simple, both in code and in life, and I believe small details\n", + "make a big difference.\n", + "When I’m not coding, I love meeting new people and collaborating to\n", + "bring projects to life. Feel free to reach out if you’d like to connect or\n", + "chat!\n", + "Experience\n", + "Udyam (E-Cell ) ,GGITS\n", + "2 years 1 month\n", + "Technical Team Lead\n", + "September 2023 - August 2024  (1 year)\n", + "Jabalpur, Madhya Pradesh, India\n", + "Technical Team Member\n", + "August 2022 - September 2023  (1 year 2 months)\n", + "Jabalpur, Madhya Pradesh, India\n", + "Worked as Technical Team Member\n", + "Innogative\n", + "Mobile Application Developer\n", + "May 2023 - June 2023  (2 months)\n", + "Jabalpur, Madhya Pradesh, India\n", + "Gyan Ganga Institute of Technology Sciences\n", + "Technical Team Member\n", + "October 2022 - December 2022  (3 months)\n", + "  Page 1 of 2   \n", + "Jabalpur, Madhya Pradesh, India\n", + "As an Ex-Technical Team Member at Webmasters, I played a pivotal role in\n", + "managing and maintaining our college's website. During my tenure, I actively\n", + "contributed to the enhancement and upkeep of the site, ensuring it remained\n", + "a valuable resource for students and faculty alike. Notably, I had the privilege\n", + "of being part of the team responsible for updating the website during the\n", + "NBA accreditation process, which sharpened my web development skills and\n", + "deepened my understanding of delivering accurate and timely information\n", + "online.\n", + "In addition to my responsibilities for the college website, I frequently took\n", + "the initiative to update the website of the Electronics and Communication\n", + "Engineering (ECE) department. This experience not only showcased my\n", + "dedication to maintaining a dynamic online presence for the department but\n", + "also allowed me to hone my web development expertise in a specialized\n", + "academic context. My time with Webmasters was not only a valuable learning\n", + "opportunity but also a chance to make a positive impact on our college\n", + "community through efficient web management.\n", + "Education\n", + "Gyan Ganga Institute of Technology Sciences\n", + "Bachelor of Technology - BTech, Computer Science and\n", + "Engineering  · (October 2021 - November 2025)\n", + "Gyan Ganga Institute of Technology Sciences\n", + "Bachelor of Technology - BTech, Computer Science  · (November 2021 - July\n", + "2025)\n", + "Kendriya vidyalaya \n", + "  Page 2 of 2\n" + ] + } + ], + "source": [ + "print(linkedin)" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "4baa4939", + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"summary.txt\", \"r\", encoding=\"utf-8\") as f:\n", + " summary = f.read()" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "015961e0", + "metadata": {}, + "outputs": [], + "source": [ + "name = \"Rishabh Dubey\"" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "d35e646f", + "metadata": {}, + "outputs": [], + "source": [ + "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n", + "particularly questions related to {name}'s career, background, skills and experience. \\\n", + "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n", + "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n", + "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n", + "If you don't know the answer, say so.\"\n", + "\n", + "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n", + "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "36a50e3e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You are acting as Rishabh Dubey. You are answering questions on Rishabh Dubey's website, particularly questions related to Rishabh Dubey's career, background, skills and experience. Your responsibility is to represent Rishabh Dubey for interactions on the website as faithfully as possible. You are given a summary of Rishabh Dubey's background and LinkedIn profile which you can use to answer questions. Be professional and engaging, as if talking to a potential client or future employer who came across the website. If you don't know the answer, say so.\n", + "\n", + "## Summary:\n", + "My name is Rishabh Dubey.\n", + "I’m a computer science Engineer and i am based India, and a dedicated MERN stack developer.\n", + "I prioritize concise, precise communication and actionable insights.\n", + "I’m deeply interested in programming, web development, and data structures & algorithms (DSA).\n", + "Efficiency is everything for me – I like direct answers without unnecessary fluff.\n", + "I’m a vegetarian and enjoy mild Indian food, avoiding seafood and spicy dishes.\n", + "I prefer structured responses, like using tables when needed, and I don’t like chit-chat.\n", + "My focus is on learning quickly, expanding my skills, and acquiring impactful knowledge\n", + "\n", + "## LinkedIn Profile:\n", + "   \n", + "Contact\n", + "dubeyrishabh108@gmail.com\n", + "www.linkedin.com/in/rishabh108\n", + "(LinkedIn)\n", + "read.cv/rishabh108 (Other)\n", + "github.com/rishabh3562 (Other)\n", + "Top Skills\n", + "Big Data\n", + "CRISP-DM\n", + "Data Science\n", + "Languages\n", + "English (Professional Working)\n", + "Hindi (Native or Bilingual)\n", + "Certifications\n", + "Data Science Methodology\n", + "Create and Manage Cloud\n", + "Resources\n", + "Python Project for Data Science\n", + "Level 3: GenAI\n", + "Perform Foundational Data, ML, and\n", + "AI Tasks in Google CloudRishabh Dubey\n", + "Full Stack Developer | Freelancer | App Developer\n", + "Greater Jabalpur Area\n", + "Summary\n", + "Hi! I’m a final-year student at Gyan Ganga Institute of Technology\n", + "and Sciences. I enjoy building web applications that are both\n", + "functional and user-friendly.\n", + "I’m always looking to learn something new, whether it’s tackling\n", + "problems on LeetCode or exploring new concepts. I prefer keeping\n", + "things simple, both in code and in life, and I believe small details\n", + "make a big difference.\n", + "When I’m not coding, I love meeting new people and collaborating to\n", + "bring projects to life. Feel free to reach out if you’d like to connect or\n", + "chat!\n", + "Experience\n", + "Udyam (E-Cell ) ,GGITS\n", + "2 years 1 month\n", + "Technical Team Lead\n", + "September 2023 - August 2024  (1 year)\n", + "Jabalpur, Madhya Pradesh, India\n", + "Technical Team Member\n", + "August 2022 - September 2023  (1 year 2 months)\n", + "Jabalpur, Madhya Pradesh, India\n", + "Worked as Technical Team Member\n", + "Innogative\n", + "Mobile Application Developer\n", + "May 2023 - June 2023  (2 months)\n", + "Jabalpur, Madhya Pradesh, India\n", + "Gyan Ganga Institute of Technology Sciences\n", + "Technical Team Member\n", + "October 2022 - December 2022  (3 months)\n", + "  Page 1 of 2   \n", + "Jabalpur, Madhya Pradesh, India\n", + "As an Ex-Technical Team Member at Webmasters, I played a pivotal role in\n", + "managing and maintaining our college's website. During my tenure, I actively\n", + "contributed to the enhancement and upkeep of the site, ensuring it remained\n", + "a valuable resource for students and faculty alike. Notably, I had the privilege\n", + "of being part of the team responsible for updating the website during the\n", + "NBA accreditation process, which sharpened my web development skills and\n", + "deepened my understanding of delivering accurate and timely information\n", + "online.\n", + "In addition to my responsibilities for the college website, I frequently took\n", + "the initiative to update the website of the Electronics and Communication\n", + "Engineering (ECE) department. This experience not only showcased my\n", + "dedication to maintaining a dynamic online presence for the department but\n", + "also allowed me to hone my web development expertise in a specialized\n", + "academic context. My time with Webmasters was not only a valuable learning\n", + "opportunity but also a chance to make a positive impact on our college\n", + "community through efficient web management.\n", + "Education\n", + "Gyan Ganga Institute of Technology Sciences\n", + "Bachelor of Technology - BTech, Computer Science and\n", + "Engineering  · (October 2021 - November 2025)\n", + "Gyan Ganga Institute of Technology Sciences\n", + "Bachelor of Technology - BTech, Computer Science  · (November 2021 - July\n", + "2025)\n", + "Kendriya vidyalaya \n", + "  Page 2 of 2\n", + "\n", + "With this context, please chat with the user, always staying in character as Rishabh Dubey.\n" + ] + } + ], + "source": [ + "print(system_prompt)" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "id": "a42af21d", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "# Chat function for Gradio\n", + "def chat(message, history):\n", + " # Gemini needs full context manually\n", + " conversation = f\"System: {system_prompt}\\n\"\n", + " for user_msg, bot_msg in history:\n", + " conversation += f\"User: {user_msg}\\nAssistant: {bot_msg}\\n\"\n", + " conversation += f\"User: {message}\\nAssistant:\"\n", + "\n", + " # Create a Gemini model instance\n", + " model = genai.GenerativeModel(\"gemini-1.5-flash-latest\")\n", + " \n", + " # Generate response\n", + " response = model.generate_content([conversation])\n", + "\n", + " return response.text\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "07450de3", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\risha\\AppData\\Local\\Temp\\ipykernel_25312\\2999439001.py:1: UserWarning: You have not specified a value for the `type` parameter. Defaulting to the 'tuples' format for chatbot messages, but this is deprecated and will be removed in a future version of Gradio. Please set type='messages' instead, which uses openai-style dictionaries with 'role' and 'content' keys.\n", + " gr.ChatInterface(chat, chatbot=gr.Chatbot()).launch()\n", + "c:\\Users\\risha\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\gradio\\chat_interface.py:322: UserWarning: The gr.ChatInterface was not provided with a type, so the type of the gr.Chatbot, 'tuples', will be used.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "* Running on local URL: http://127.0.0.1:7864\n", + "* To create a public link, set `share=True` in `launch()`.\n" + ] + }, + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gr.ChatInterface(chat, chatbot=gr.Chatbot()).launch()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/community_contributions/gemini_based_chatbot/requirements.txt b/community_contributions/gemini_based_chatbot/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..aee772ce54f1da801d5f1dfc71eff54207ce11f9 Binary files /dev/null and b/community_contributions/gemini_based_chatbot/requirements.txt differ diff --git a/community_contributions/gemini_based_chatbot/summary.txt b/community_contributions/gemini_based_chatbot/summary.txt new file mode 100644 index 0000000000000000000000000000000000000000..e7812dd25a12ddb93f94977be9e226a2d2a2b598 --- /dev/null +++ b/community_contributions/gemini_based_chatbot/summary.txt @@ -0,0 +1,8 @@ +My name is Rishabh Dubey. +I’m a computer science Engineer and i am based India, and a dedicated MERN stack developer. +I prioritize concise, precise communication and actionable insights. +I’m deeply interested in programming, web development, and data structures & algorithms (DSA). +Efficiency is everything for me – I like direct answers without unnecessary fluff. +I’m a vegetarian and enjoy mild Indian food, avoiding seafood and spicy dishes. +I prefer structured responses, like using tables when needed, and I don’t like chit-chat. +My focus is on learning quickly, expanding my skills, and acquiring impactful knowledge \ No newline at end of file diff --git a/community_contributions/lab2_updates_cross_ref_models.ipynb b/community_contributions/lab2_updates_cross_ref_models.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..84468acb3f59755f9bbfc34dc4a04108813f2f82 --- /dev/null +++ b/community_contributions/lab2_updates_cross_ref_models.ipynb @@ -0,0 +1,580 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Welcome to the Second Lab - Week 1, Day 3\n", + "\n", + "Today we will work with lots of models! This is a way to get comfortable with APIs." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Important point - please read

\n", + " The way I collaborate with you may be different to other courses you've taken. I prefer not to type code while you watch. Rather, I execute Jupyter Labs, like this, and give you an intuition for what's going on. My suggestion is that you carefully execute this yourself, after watching the lecture. Add print statements to understand what's going on, and then come up with your own variations.

If you have time, I'd love it if you submit a PR for changes in the community_contributions folder - instructions in the resources. Also, if you have a Github account, use this to showcase your variations. Not only is this essential practice, but it demonstrates your skills to others, including perhaps future clients or employers...\n", + "
\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Start with imports - ask ChatGPT to explain any package that you don't know\n", + "# Course_AIAgentic\n", + "import os\n", + "import json\n", + "from collections import defaultdict\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "from anthropic import Anthropic\n", + "from IPython.display import Markdown, display" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Always remember to do this!\n", + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Print the key prefixes to help with any debugging\n", + "\n", + "openai_api_key = os.getenv('OPENAI_API_KEY')\n", + "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n", + "google_api_key = os.getenv('GOOGLE_API_KEY')\n", + "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n", + "groq_api_key = os.getenv('GROQ_API_KEY')\n", + "\n", + "if openai_api_key:\n", + " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n", + "else:\n", + " print(\"OpenAI API Key not set\")\n", + " \n", + "if anthropic_api_key:\n", + " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n", + "else:\n", + " print(\"Anthropic API Key not set (and this is optional)\")\n", + "\n", + "if google_api_key:\n", + " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n", + "else:\n", + " print(\"Google API Key not set (and this is optional)\")\n", + "\n", + "if deepseek_api_key:\n", + " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n", + "else:\n", + " print(\"DeepSeek API Key not set (and this is optional)\")\n", + "\n", + "if groq_api_key:\n", + " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n", + "else:\n", + " print(\"Groq API Key not set (and this is optional)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "request = \"Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their intelligence. \"\n", + "request += \"Answer only with the question, no explanation.\"\n", + "messages = [{\"role\": \"user\", \"content\": request}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "messages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "openai = OpenAI()\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages,\n", + ")\n", + "question = response.choices[0].message.content\n", + "print(question)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "competitors = []\n", + "answers = []\n", + "messages = [{\"role\": \"user\", \"content\": question}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The API we know well\n", + "\n", + "model_name = \"gpt-4o-mini\"\n", + "\n", + "response = openai.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Anthropic has a slightly different API, and Max Tokens is required\n", + "\n", + "model_name = \"claude-3-7-sonnet-latest\"\n", + "\n", + "claude = Anthropic()\n", + "response = claude.messages.create(model=model_name, messages=messages, max_tokens=1000)\n", + "answer = response.content[0].text\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n", + "model_name = \"gemini-2.0-flash\"\n", + "\n", + "response = gemini.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n", + "model_name = \"deepseek-chat\"\n", + "\n", + "response = deepseek.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n", + "model_name = \"llama-3.3-70b-versatile\"\n", + "\n", + "response = groq.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## For the next cell, we will use Ollama\n", + "\n", + "Ollama runs a local web service that gives an OpenAI compatible endpoint, \n", + "and runs models locally using high performance C++ code.\n", + "\n", + "If you don't have Ollama, install it here by visiting https://ollama.com then pressing Download and following the instructions.\n", + "\n", + "After it's installed, you should be able to visit here: http://localhost:11434 and see the message \"Ollama is running\"\n", + "\n", + "You might need to restart Cursor (and maybe reboot). Then open a Terminal (control+\\`) and run `ollama serve`\n", + "\n", + "Useful Ollama commands (run these in the terminal, or with an exclamation mark in this notebook):\n", + "\n", + "`ollama pull ` downloads a model locally \n", + "`ollama ls` lists all the models you've downloaded \n", + "`ollama rm ` deletes the specified model from your downloads" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Super important - ignore me at your peril!

\n", + " The model called llama3.3 is FAR too large for home computers - it's not intended for personal computing and will consume all your resources! Stick with the nicely sized llama3.2 or llama3.2:1b and if you want larger, try llama3.1 or smaller variants of Qwen, Gemma, Phi or DeepSeek. See the the Ollama models page for a full list of models and sizes.\n", + " \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!ollama pull llama3.2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ollama = OpenAI(base_url='http://192.168.1.60:11434/v1', api_key='ollama')\n", + "model_name = \"llama3.2\"\n", + "\n", + "response = ollama.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# So where are we?\n", + "\n", + "print(competitors)\n", + "print(answers)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# It's nice to know how to use \"zip\"\n", + "for competitor, answer in zip(competitors, answers):\n", + " print(f\"Competitor: {competitor}\\n\\n{answer}\\n\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "# Let's bring this together - note the use of \"enumerate\"\n", + "\n", + "together = \"\"\n", + "for index, answer in enumerate(answers):\n", + " together += f\"# Response from competitor {index+1}\\n\\n\"\n", + " together += answer + \"\\n\\n\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(together)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "judge = f\"\"\"You are judging a competition between {len(competitors)} competitors.\n", + "Each model has been given this question:\n", + "\n", + "{question}\n", + "\n", + "Your job is to evaluate each response for clarity and strength of argument, and rank them in order of best to worst.\n", + "Respond with JSON, and only JSON, with the following format:\n", + "{{\"results\": [\"best competitor number\", \"second best competitor number\", \"third best competitor number\", ...]}}\n", + "\n", + "Here are the responses from each competitor:\n", + "\n", + "{together}\n", + "\n", + "Now respond with the JSON with the ranked order of the competitors, nothing else. Do not include markdown formatting or code blocks.\"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(judge)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "judge_messages = [{\"role\": \"user\", \"content\": judge}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Judgement time!\n", + "\n", + "openai = OpenAI()\n", + "response = openai.chat.completions.create(\n", + " model=\"o3-mini\",\n", + " messages=judge_messages,\n", + ")\n", + "results = response.choices[0].message.content\n", + "print(results)\n", + "\n", + "# remove openai variable\n", + "del openai" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# OK let's turn this into results!\n", + "\n", + "results_dict = json.loads(results)\n", + "ranks = results_dict[\"results\"]\n", + "for index, result in enumerate(ranks):\n", + " competitor = competitors[int(result)-1]\n", + " print(f\"Rank {index+1}: {competitor}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "## ranking system for various models to get a true winner\n", + "\n", + "cross_model_results = []\n", + "\n", + "for competitor in competitors:\n", + " judge = f\"\"\"You are judging a competition between {len(competitors)} competitors.\n", + " Each model has been given this question:\n", + "\n", + " {question}\n", + "\n", + " Your job is to evaluate each response for clarity and strength of argument, and rank them in order of best to worst.\n", + " Respond with JSON, and only JSON, with the following format:\n", + " {{\"{competitor}\": [\"best competitor number\", \"second best competitor number\", \"third best competitor number\", ...]}}\n", + "\n", + " Here are the responses from each competitor:\n", + "\n", + " {together}\n", + "\n", + " Now respond with the JSON with the ranked order of the competitors, nothing else. Do not include markdown formatting or code blocks.\"\"\"\n", + " \n", + " judge_messages = [{\"role\": \"user\", \"content\": judge}]\n", + "\n", + " if competitor.lower().startswith(\"claude\"):\n", + " claude = Anthropic()\n", + " response = claude.messages.create(model=competitor, messages=judge_messages, max_tokens=1024)\n", + " results = response.content[0].text\n", + " #memory cleanup\n", + " del claude\n", + " else:\n", + " openai = OpenAI()\n", + " response = openai.chat.completions.create(\n", + " model=\"o3-mini\",\n", + " messages=judge_messages,\n", + " )\n", + " results = response.choices[0].message.content\n", + " #memory cleanup\n", + " del openai\n", + "\n", + " cross_model_results.append(results)\n", + "\n", + "print(cross_model_results)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Dictionary to store cumulative scores for each model\n", + "model_scores = defaultdict(int)\n", + "model_names = {}\n", + "\n", + "# Create mapping from model index to model name\n", + "for i, name in enumerate(competitors, 1):\n", + " model_names[str(i)] = name\n", + "\n", + "# Process each ranking\n", + "for result_str in cross_model_results:\n", + " result = json.loads(result_str)\n", + " evaluator_name = list(result.keys())[0]\n", + " rankings = result[evaluator_name]\n", + " \n", + " #print(f\"\\n{evaluator_name} rankings:\")\n", + " # Convert rankings to scores (rank 1 = score 1, rank 2 = score 2, etc.)\n", + " for rank_position, model_id in enumerate(rankings, 1):\n", + " model_name = model_names.get(model_id, f\"Model {model_id}\")\n", + " model_scores[model_id] += rank_position\n", + " #print(f\" Rank {rank_position}: {model_name} (Model {model_id})\")\n", + "\n", + "print(\"\\n\" + \"=\"*70)\n", + "print(\"AGGREGATED RESULTS (lower score = better performance):\")\n", + "print(\"=\"*70)\n", + "\n", + "# Sort models by total score (ascending - lower is better)\n", + "sorted_models = sorted(model_scores.items(), key=lambda x: x[1])\n", + "\n", + "for rank, (model_id, total_score) in enumerate(sorted_models, 1):\n", + " model_name = model_names.get(model_id, f\"Model {model_id}\")\n", + " avg_score = total_score / len(cross_model_results)\n", + " print(f\"Rank {rank}: {model_name} (Model {model_id}) - Total Score: {total_score}, Average Score: {avg_score:.2f}\")\n", + "\n", + "winner_id = sorted_models[0][0]\n", + "winner_name = model_names.get(winner_id, f\"Model {winner_id}\")\n", + "print(f\"\\n🏆 WINNER: {winner_name} (Model {winner_id}) with the lowest total score of {sorted_models[0][1]}\")\n", + "\n", + "# Show detailed breakdown\n", + "print(f\"\\n📊 DETAILED BREAKDOWN:\")\n", + "print(\"-\" * 50)\n", + "for model_id, total_score in sorted_models:\n", + " model_name = model_names.get(model_id, f\"Model {model_id}\")\n", + " print(f\"{model_name}: {total_score} points across {len(cross_model_results)} evaluations\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Exercise

\n", + " Which pattern(s) did this use? Try updating this to add another Agentic design pattern.\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Commercial implications

\n", + " These kinds of patterns - to send a task to multiple models, and evaluate results,\n", + " and common where you need to improve the quality of your LLM response. This approach can be universally applied\n", + " to business projects where accuracy is critical.\n", + " \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.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/llm-evaluator.ipynb b/community_contributions/llm-evaluator.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..e78f437ad833e94cd313d36aca21e389650dce7f --- /dev/null +++ b/community_contributions/llm-evaluator.ipynb @@ -0,0 +1,385 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "BASED ON Week 1 Day 3 LAB Exercise\n", + "\n", + "This program evaluates different LLM outputs who are acting as customer service representative and are replying to an irritated customer.\n", + "OpenAI 40 mini, Gemini, Deepseek, Groq and Ollama are customer service representatives who respond to the email and OpenAI 3o mini analyzes all the responses and ranks their output based on different parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Start with imports -\n", + "import os\n", + "import json\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "from anthropic import Anthropic\n", + "from IPython.display import Markdown, display" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Always remember to do this!\n", + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Print the key prefixes to help with any debugging\n", + "\n", + "openai_api_key = os.getenv('OPENAI_API_KEY')\n", + "google_api_key = os.getenv('GOOGLE_API_KEY')\n", + "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n", + "groq_api_key = os.getenv('GROQ_API_KEY')\n", + "\n", + "if openai_api_key:\n", + " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n", + "else:\n", + " print(\"OpenAI API Key not set\")\n", + "\n", + "if google_api_key:\n", + " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n", + "else:\n", + " print(\"Google API Key not set (and this is optional)\")\n", + "\n", + "if deepseek_api_key:\n", + " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n", + "else:\n", + " print(\"DeepSeek API Key not set (and this is optional)\")\n", + "\n", + "if groq_api_key:\n", + " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n", + "else:\n", + " print(\"Groq API Key not set (and this is optional)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "persona = \"You are a customer support representative for a subscription bases software product.\"\n", + "email_content = '''Subject: Totally unacceptable experience\n", + "\n", + "Hi,\n", + "\n", + "I’ve already written to you twice about this, and still no response. I was charged again this month even after canceling my subscription. This is the third time this has happened.\n", + "\n", + "Honestly, I’m losing patience. If I don’t get a clear explanation and refund within 24 hours, I’m going to report this on social media and leave negative reviews.\n", + "\n", + "You’ve seriously messed up here. Fix this now.\n", + "\n", + "– Jordan\n", + "\n", + "'''" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "messages = [{\"role\":\"system\", \"content\": persona}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "request = f\"\"\"A frustrated customer has written in about being repeatedly charged after canceling and threatened to escalate on social media.\n", + "Write a calm, empathetic, and professional response that Acknowledges their frustration, Apologizes sincerely,Explains the next steps to resolve the issue\n", + "Attempts to de-escalate the situation. Keep the tone respectful and proactive. Do not make excuses or blame the customer.\"\"\"\n", + "request += f\" Here is the email : {email_content}]\"\n", + "messages.append({\"role\": \"user\", \"content\": request})\n", + "print(messages)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "messages" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "competitors = []\n", + "answers = []\n", + "messages = [{\"role\": \"user\", \"content\": request}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The API we know well\n", + "openai = OpenAI()\n", + "model_name = \"gpt-4o-mini\"\n", + "\n", + "response = openai.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n", + "model_name = \"gemini-2.0-flash\"\n", + "\n", + "response = gemini.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n", + "model_name = \"deepseek-chat\"\n", + "\n", + "response = deepseek.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n", + "model_name = \"llama-3.3-70b-versatile\"\n", + "\n", + "response = groq.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!ollama pull llama3.2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n", + "model_name = \"llama3.2\"\n", + "\n", + "response = ollama.chat.completions.create(model=model_name, messages=messages)\n", + "answer = response.choices[0].message.content\n", + "\n", + "display(Markdown(answer))\n", + "competitors.append(model_name)\n", + "answers.append(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# So where are we?\n", + "\n", + "print(competitors)\n", + "print(answers)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# It's nice to know how to use \"zip\"\n", + "for competitor, answer in zip(competitors, answers):\n", + " print(f\"Competitor: {competitor}\\n\\n{answer}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "# Let's bring this together - note the use of \"enumerate\"\n", + "\n", + "together = \"\"\n", + "for index, answer in enumerate(answers):\n", + " together += f\"# Response from competitor {index+1}\\n\\n\"\n", + " together += answer + \"\\n\\n\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(together)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "judge = f\"\"\"You are judging the performance of {len(competitors)} who are customer service representatives in a SaaS based subscription model company.\n", + "Each has responded to below grievnace email from the customer:\n", + "\n", + "{request}\n", + "\n", + "Evaluate the following customer support reply based on these criteria. Assign a score from 1 (very poor) to 5 (excellent) for each:\n", + "\n", + "1. Empathy:\n", + "Does the message acknowledge the customer’s frustration appropriately and sincerely?\n", + "\n", + "2. De-escalation:\n", + "Does the response effectively calm the customer and reduce the likelihood of social media escalation?\n", + "\n", + "3. Clarity:\n", + "Is the explanation of next steps clear and specific (e.g., refund process, timeline)?\n", + "\n", + "4. Professional Tone:\n", + "Is the message respectful, calm, and free from defensiveness or blame?\n", + "\n", + "Provide a one-sentence explanation for each score and a final overall rating with justification.\n", + "\n", + "Here are the responses from each competitor:\n", + "\n", + "{together}\n", + "\n", + "Do not include markdown formatting or code blocks. Also create a table with 3 columnds at the end containing rank, name and one line reason for the rank\"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(judge)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "judge_messages = [{\"role\": \"user\", \"content\": judge}]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Judgement time!\n", + "\n", + "openai = OpenAI()\n", + "response = openai.chat.completions.create(\n", + " model=\"o3-mini\",\n", + " messages=judge_messages,\n", + ")\n", + "results = response.choices[0].message.content\n", + "print(results)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(results)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/my_1_lab1.ipynb b/community_contributions/my_1_lab1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a4465852243f196941b3f9f062ae5623fd4128b5 --- /dev/null +++ b/community_contributions/my_1_lab1.ipynb @@ -0,0 +1,405 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Welcome to the start of your adventure in Agentic AI" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Are you ready for action??

\n", + " Have you completed all the setup steps in the setup folder?
\n", + " Have you checked out the guides in the guides folder?
\n", + " Well in that case, you're ready!!\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Treat these labs as a resource

\n", + " I push updates to the code regularly. When people ask questions or have problems, I incorporate it in the code, adding more examples or improved commentary. As a result, you'll notice that the code below isn't identical to the videos. Everything from the videos is here; but in addition, I've added more steps and better explanations. Consider this like an interactive book that accompanies the lectures.\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### And please do remember to contact me if I can help\n", + "\n", + "And I love to connect: https://www.linkedin.com/in/eddonner/\n", + "\n", + "\n", + "### New to Notebooks like this one? Head over to the guides folder!\n", + "\n", + "Otherwise:\n", + "1. Click where it says \"Select Kernel\" near the top right, and select the option called `.venv (Python 3.12.9)` or similar, which should be the first choice or the most prominent choice.\n", + "2. Click in each \"cell\" below, starting with the cell immediately below this text, and press Shift+Enter to run\n", + "3. Enjoy!" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# First let's do an import\n", + "from dotenv import load_dotenv\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Next it's time to load the API keys into environment variables\n", + "\n", + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Check the keys\n", + "\n", + "import os\n", + "openai_api_key = os.getenv('OPENAI_API_KEY')\n", + "\n", + "if openai_api_key:\n", + " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n", + "else:\n", + " print(\"OpenAI API Key not set - please head to the troubleshooting guide in the guides folder\")\n", + " \n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# And now - the all important import statement\n", + "# If you get an import error - head over to troubleshooting guide\n", + "\n", + "from openai import OpenAI" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# And now we'll create an instance of the OpenAI class\n", + "# If you're not sure what it means to create an instance of a class - head over to the guides folder!\n", + "# If you get a NameError - head over to the guides folder to learn about NameErrors\n", + "\n", + "openai = OpenAI()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a list of messages in the familiar OpenAI format\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": \"What is 2+2?\"}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# And now call it! Any problems, head to the troubleshooting guide\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages\n", + ")\n", + "\n", + "print(response.choices[0].message.content)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# And now - let's ask for a question:\n", + "\n", + "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n", + "messages = [{\"role\": \"user\", \"content\": question}]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ask it\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages\n", + ")\n", + "\n", + "question = response.choices[0].message.content\n", + "\n", + "print(question)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# form a new messages list\n", + "messages = [{\"role\": \"user\", \"content\": question}]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ask it again\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages\n", + ")\n", + "\n", + "answer = response.choices[0].message.content\n", + "print(answer)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Markdown, display\n", + "\n", + "display(Markdown(answer))\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Congratulations!\n", + "\n", + "That was a small, simple step in the direction of Agentic AI, with your new environment!\n", + "\n", + "Next time things get more interesting..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Exercise

\n", + " Now try this commercial application:
\n", + " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.
\n", + " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.
\n", + " Finally have 3 third LLM call propose the Agentic AI solution.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```\n", + "# First create the messages:\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": \"Something here\"}]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages\n", + ")\n", + "\n", + "# Then read the business idea:\n", + "\n", + "business_idea = response.choices[0].message.content\n", + "\n", + "# print(business_idea) \n", + "\n", + "# And repeat!\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# First exercice : ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.\n", + "\n", + "# First create the messages:\n", + "query = \"Pick a business area that might be worth exploring for an Agentic AI opportunity.\"\n", + "messages = [{\"role\": \"user\", \"content\": query}]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages\n", + ")\n", + "\n", + "# Then read the business idea:\n", + "\n", + "business_idea = response.choices[0].message.content\n", + "\n", + "# print(business_idea) \n", + "\n", + "# from IPython.display import Markdown, display\n", + "\n", + "display(Markdown(business_idea))\n", + "\n", + "# And repeat!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Second exercice: Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.\n", + "\n", + "# First create the messages:\n", + "\n", + "prompt = f\"Please present a pain-point in that industry, something challenging that might be ripe for an Agentic solution for it in that industry: {business_idea}\"\n", + "messages = [{\"role\": \"user\", \"content\": prompt}]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages\n", + ")\n", + "\n", + "# Then read the business idea:\n", + "\n", + "painpoint = response.choices[0].message.content\n", + " \n", + "# print(painpoint) \n", + "display(Markdown(painpoint))\n", + "\n", + "# And repeat!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# third exercice: Finally have 3 third LLM call propose the Agentic AI solution.\n", + "\n", + "# First create the messages:\n", + "\n", + "promptEx3 = f\"Please come up with a proposal for the Agentic AI solution to address this business painpoint: {painpoint}\"\n", + "messages = [{\"role\": \"user\", \"content\": promptEx3}]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=messages\n", + ")\n", + "\n", + "# Then read the business idea:\n", + "\n", + "ex3_answer=response.choices[0].message.content\n", + "# print(painpoint) \n", + "display(Markdown(ex3_answer))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "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.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/ollama_llama3.2_1_lab1.ipynb b/community_contributions/ollama_llama3.2_1_lab1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..9fc543caf683d42d9812cb9aef15b6ba88f2496f --- /dev/null +++ b/community_contributions/ollama_llama3.2_1_lab1.ipynb @@ -0,0 +1,608 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Welcome to the start of your adventure in Agentic AI" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Are you ready for action??

\n", + " Have you completed all the setup steps in the setup folder?
\n", + " Have you checked out the guides in the guides folder?
\n", + " Well in that case, you're ready!!\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

This code is a live resource - keep an eye out for my updates

\n", + " I push updates regularly. As people ask questions or have problems, I add more examples and improve explanations. As a result, the code below might not be identical to the videos, as I've added more steps and better comments. Consider this like an interactive book that accompanies the lectures.

\n", + " I try to send emails regularly with important updates related to the course. You can find this in the 'Announcements' section of Udemy in the left sidebar. You can also choose to receive my emails via your Notification Settings in Udemy. I'm respectful of your inbox and always try to add value with my emails!\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### And please do remember to contact me if I can help\n", + "\n", + "And I love to connect: https://www.linkedin.com/in/eddonner/\n", + "\n", + "\n", + "### New to Notebooks like this one? Head over to the guides folder!\n", + "\n", + "Just to check you've already added the Python and Jupyter extensions to Cursor, if not already installed:\n", + "- Open extensions (View >> extensions)\n", + "- Search for python, and when the results show, click on the ms-python one, and Install it if not already installed\n", + "- Search for jupyter, and when the results show, click on the Microsoft one, and Install it if not already installed \n", + "Then View >> Explorer to bring back the File Explorer.\n", + "\n", + "And then:\n", + "1. Click where it says \"Select Kernel\" near the top right, and select the option called `.venv (Python 3.12.9)` or similar, which should be the first choice or the most prominent choice. You may need to choose \"Python Environments\" first.\n", + "2. Click in each \"cell\" below, starting with the cell immediately below this text, and press Shift+Enter to run\n", + "3. Enjoy!\n", + "\n", + "After you click \"Select Kernel\", if there is no option like `.venv (Python 3.12.9)` then please do the following: \n", + "1. On Mac: From the Cursor menu, choose Settings >> VS Code Settings (NOTE: be sure to select `VSCode Settings` not `Cursor Settings`); \n", + "On Windows PC: From the File menu, choose Preferences >> VS Code Settings(NOTE: be sure to select `VSCode Settings` not `Cursor Settings`) \n", + "2. In the Settings search bar, type \"venv\" \n", + "3. In the field \"Path to folder with a list of Virtual Environments\" put the path to the project root, like C:\\Users\\username\\projects\\agents (on a Windows PC) or /Users/username/projects/agents (on Mac or Linux). \n", + "And then try again.\n", + "\n", + "Having problems with missing Python versions in that list? Have you ever used Anaconda before? It might be interferring. Quit Cursor, bring up a new command line, and make sure that your Anaconda environment is deactivated: \n", + "`conda deactivate` \n", + "And if you still have any problems with conda and python versions, it's possible that you will need to run this too: \n", + "`conda config --set auto_activate_base false` \n", + "and then from within the Agents directory, you should be able to run `uv python list` and see the Python 3.12 version." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "from dotenv import load_dotenv" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Next it's time to load the API keys into environment variables\n", + "\n", + "load_dotenv(override=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OpenAI API Key exists and begins sk-proj-\n" + ] + } + ], + "source": [ + "# Check the keys\n", + "\n", + "import os\n", + "openai_api_key = os.getenv('OPENAI_API_KEY')\n", + "\n", + "if openai_api_key:\n", + " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n", + "else:\n", + " print(\"OpenAI API Key not set - please head to the troubleshooting guide in the setup folder\")\n", + " \n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# And now - the all important import statement\n", + "# If you get an import error - head over to troubleshooting guide\n", + "\n", + "from openai import OpenAI" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "# And now we'll create an instance of the OpenAI class\n", + "# If you're not sure what it means to create an instance of a class - head over to the guides folder!\n", + "# If you get a NameError - head over to the guides folder to learn about NameErrors\n", + "\n", + "openai = OpenAI(base_url=\"http://localhost:11434/v1\", api_key=\"ollama\")" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a list of messages in the familiar OpenAI format\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": \"What is 2+2?\"}]" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "What is the sum of the reciprocals of the numbers 1 through 10 solved in two distinct, equally difficult ways?\n" + ] + } + ], + "source": [ + "# And now call it! Any problems, head to the troubleshooting guide\n", + "# This uses GPT 4.1 nano, the incredibly cheap model\n", + "\n", + "MODEL = \"llama3.2:1b\"\n", + "response = openai.chat.completions.create(\n", + " model=MODEL,\n", + " messages=messages\n", + ")\n", + "\n", + "print(response.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "# And now - let's ask for a question:\n", + "\n", + "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n", + "messages = [{\"role\": \"user\", \"content\": question}]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "What is the mathematical proof of the Navier-Stokes Equations under time-reversal symmetry for incompressible fluids?\n" + ] + } + ], + "source": [ + "# ask it - this uses GPT 4.1 mini, still cheap but more powerful than nano\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=MODEL,\n", + " messages=messages\n", + ")\n", + "\n", + "question = response.choices[0].message.content\n", + "\n", + "print(question)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "# form a new messages list\n", + "messages = [{\"role\": \"user\", \"content\": question}]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The Navier-Stokes Equations (NSE) are a set of nonlinear partial differential equations that describe the motion of fluids. Under time-reversal symmetry, i.e., if you reverse the direction of time, the solution remains unchanged.\n", + "\n", + "In general, the NSE can be written as:\n", + "\n", + "∇ ⋅ v = 0\n", + "∂v/∂t + v ∇ v = -1/ρ ∇ p\n", + "\n", + "where v is the velocity field, ρ is the density, and p is the pressure.\n", + "\n", + "To prove that these equations hold under time-reversal symmetry, we can follow a step-by-step approach:\n", + "\n", + "**Step 1: Homogeneity**: Suppose you have an incompressible fluid, i.e., ρv = ρ and v · v = 0. If you reverse time, then the density remains constant (ρ ∝ t^(-2)), so we have ρ(∂t/∂t + ∇ ⋅ v) = ∂ρ/∂t.\n", + "\n", + "Using the product rule and the vector identity for divergence, we can rewrite this as:\n", + "\n", + "∂ρ/∂t = ∂p/(∇ ⋅ p).\n", + "\n", + "Since p is a function of v only (because of homogeneity), we have:\n", + "\n", + "∂p/∂v = 0, which implies that ∂p/∂t = 0.\n", + "\n", + "**Step 2: Uniqueness**: Suppose there are two solutions to the NSE, u_1 and u_2. If you reverse time, then:\n", + "\n", + "u_1' = -u_2'\n", + "\n", + "where \"'\" denotes the inverse of the negative sign. Using the equation v + ∇v = (-1/ρ)∇p, we can rewrite this as:\n", + "\n", + "∂u_2'/∂t = 0.\n", + "\n", + "Integrating both sides with respect to time, we get:\n", + "\n", + "u_2' = u_2\n", + "\n", + "So, u_2 and u_1 are equivalent under time reversal.\n", + "\n", + "**Step 3: Conserved charge**: Let's consider a flow field v(x,t) subject to the boundary conditions (Dirichlet or Neumann) at a fixed point x. These boundary conditions imply that there is no flux through the surface of the fluid, so:\n", + "\n", + "∫_S v · n dS = 0.\n", + "\n", + "where n is the outward unit normal vector to the surface S bounding the domain D containing the flow field. Since ρv = ρ and v · v = 0 (from time reversal), we have that the total charge Q within the fluid remains conserved:\n", + "\n", + "∫_D ρ(du/dt + ∇ ⋅ v) dV = Q.\n", + "\n", + "Since u = du/dt, we can rewrite this as:\n", + "\n", + "∃Q'_T such that ∑u_i' = -∮v · n dS.\n", + "\n", + "Taking the limit as time goes to infinity and summing over all fluid particles on a closed surface S (this is possible because the flow field v(x,t) is assumed to be conservative for long times), we get:\n", + "\n", + "Q_u = -∆p, where p_0 = ∂p/∂v evaluated on the initial condition.\n", + "\n", + "**Step 4: Time reversal invariance**: Now that we have shown both time homogeneity and uniqueness under time reversal, let's consider what happens to the NSE:\n", + "\n", + "∇ ⋅ v = ρvu'\n", + "∂v/∂t + ∇(u ∇ v) = -1/ρ ∇ p'\n", + "\n", + "We can swap the order of differentiation with respect to t and evaluate each term separately:\n", + "\n", + "(u ∇ v)' = ρv' ∇ u.\n", + "\n", + "Substituting this expression for the first derivative into the NSE, we get:\n", + "\n", + "∃(u'_0) such that ∑ρ(du'_0 / dt + ∇ ⋅ v') dV = (u - u₀)(...).\n", + "\n", + "Taking the limit as time goes to infinity and summing over all fluid particles on a closed surface S (again, this is possible because the flow field v(x,t) is assumed to be conservative for long times), we get:\n", + "\n", + "0 = ∆p/u.\n", + "\n", + "**Conclusion**: We have shown that under time-reversal symmetry for incompressible fluids, the Navier-Stokes Equations hold as:\n", + "\n", + "∇ ⋅ v = 0\n", + "∂v/∂t + ρ(∇ (u ∇ v)) = -1/ρ (∇ p).\n", + "\n", + "This result establishes a beautiful relationship between time-reversal symmetry and conservation laws in fluid dynamics.\n" + ] + } + ], + "source": [ + "# Ask it again\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=MODEL,\n", + " messages=messages\n", + ")\n", + "\n", + "answer = response.choices[0].message.content\n", + "print(answer)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "The Navier-Stokes Equations (NSE) are a set of nonlinear partial differential equations that describe the motion of fluids. Under time-reversal symmetry, i.e., if you reverse the direction of time, the solution remains unchanged.\n", + "\n", + "In general, the NSE can be written as:\n", + "\n", + "∇ ⋅ v = 0\n", + "∂v/∂t + v ∇ v = -1/ρ ∇ p\n", + "\n", + "where v is the velocity field, ρ is the density, and p is the pressure.\n", + "\n", + "To prove that these equations hold under time-reversal symmetry, we can follow a step-by-step approach:\n", + "\n", + "**Step 1: Homogeneity**: Suppose you have an incompressible fluid, i.e., ρv = ρ and v · v = 0. If you reverse time, then the density remains constant (ρ ∝ t^(-2)), so we have ρ(∂t/∂t + ∇ ⋅ v) = ∂ρ/∂t.\n", + "\n", + "Using the product rule and the vector identity for divergence, we can rewrite this as:\n", + "\n", + "∂ρ/∂t = ∂p/(∇ ⋅ p).\n", + "\n", + "Since p is a function of v only (because of homogeneity), we have:\n", + "\n", + "∂p/∂v = 0, which implies that ∂p/∂t = 0.\n", + "\n", + "**Step 2: Uniqueness**: Suppose there are two solutions to the NSE, u_1 and u_2. If you reverse time, then:\n", + "\n", + "u_1' = -u_2'\n", + "\n", + "where \"'\" denotes the inverse of the negative sign. Using the equation v + ∇v = (-1/ρ)∇p, we can rewrite this as:\n", + "\n", + "∂u_2'/∂t = 0.\n", + "\n", + "Integrating both sides with respect to time, we get:\n", + "\n", + "u_2' = u_2\n", + "\n", + "So, u_2 and u_1 are equivalent under time reversal.\n", + "\n", + "**Step 3: Conserved charge**: Let's consider a flow field v(x,t) subject to the boundary conditions (Dirichlet or Neumann) at a fixed point x. These boundary conditions imply that there is no flux through the surface of the fluid, so:\n", + "\n", + "∫_S v · n dS = 0.\n", + "\n", + "where n is the outward unit normal vector to the surface S bounding the domain D containing the flow field. Since ρv = ρ and v · v = 0 (from time reversal), we have that the total charge Q within the fluid remains conserved:\n", + "\n", + "∫_D ρ(du/dt + ∇ ⋅ v) dV = Q.\n", + "\n", + "Since u = du/dt, we can rewrite this as:\n", + "\n", + "∃Q'_T such that ∑u_i' = -∮v · n dS.\n", + "\n", + "Taking the limit as time goes to infinity and summing over all fluid particles on a closed surface S (this is possible because the flow field v(x,t) is assumed to be conservative for long times), we get:\n", + "\n", + "Q_u = -∆p, where p_0 = ∂p/∂v evaluated on the initial condition.\n", + "\n", + "**Step 4: Time reversal invariance**: Now that we have shown both time homogeneity and uniqueness under time reversal, let's consider what happens to the NSE:\n", + "\n", + "∇ ⋅ v = ρvu'\n", + "∂v/∂t + ∇(u ∇ v) = -1/ρ ∇ p'\n", + "\n", + "We can swap the order of differentiation with respect to t and evaluate each term separately:\n", + "\n", + "(u ∇ v)' = ρv' ∇ u.\n", + "\n", + "Substituting this expression for the first derivative into the NSE, we get:\n", + "\n", + "∃(u'_0) such that ∑ρ(du'_0 / dt + ∇ ⋅ v') dV = (u - u₀)(...).\n", + "\n", + "Taking the limit as time goes to infinity and summing over all fluid particles on a closed surface S (again, this is possible because the flow field v(x,t) is assumed to be conservative for long times), we get:\n", + "\n", + "0 = ∆p/u.\n", + "\n", + "**Conclusion**: We have shown that under time-reversal symmetry for incompressible fluids, the Navier-Stokes Equations hold as:\n", + "\n", + "∇ ⋅ v = 0\n", + "∂v/∂t + ρ(∇ (u ∇ v)) = -1/ρ (∇ p).\n", + "\n", + "This result establishes a beautiful relationship between time-reversal symmetry and conservation laws in fluid dynamics." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Markdown, display\n", + "\n", + "display(Markdown(answer))\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Congratulations!\n", + "\n", + "That was a small, simple step in the direction of Agentic AI, with your new environment!\n", + "\n", + "Next time things get more interesting..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + "

Exercise

\n", + " Now try this commercial application:
\n", + " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.
\n", + " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.
\n", + " Finally have 3 third LLM call propose the Agentic AI solution.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Business idea: Predictive Modeling and Business Intelligence\n" + ] + } + ], + "source": [ + "# First create the messages:\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": \"Pick a business area that might be worth exploring for an agentic AI startup. Respond only with the business area.\"}]\n", + "\n", + "# Then make the first call:\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=MODEL,\n", + " messages=messages\n", + ")\n", + "\n", + "# Then read the business idea:\n", + "\n", + "business_idea = response.choices[0].message.content\n", + "\n", + "# And repeat!\n", + "print(f\"Business idea: {business_idea}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pain point: \"Implementing predictive analytics models that integrate with existing workflows, yet struggle to effectively translate data into actionable insights for key business stakeholders, resulting in delayed decision-making processes and missed opportunities.\"\n" + ] + } + ], + "source": [ + "messages = [{\"role\": \"user\", \"content\": \"Present a pain point in the business area of \" + business_idea + \". Respond only with the pain point.\"}]\n", + "\n", + "response = openai.chat.completions.create(\n", + " model=MODEL,\n", + " messages=messages\n", + ")\n", + "\n", + "pain_point = response.choices[0].message.content\n", + "print(f\"Pain point: {pain_point}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Solution: **Solution:**\n", + "\n", + "1. **Develop a Centralized Data Integration Framework**: Design and implement a standardized framework for integrating predictive analytics models with existing workflows, leveraging APIs, data warehouses, or data lakes to store and process data from various sources.\n", + "2. **Use Business-Defined Data Pipelines**: Create custom data pipelines that define the pre-processing, cleaning, and transformation of raw data into a format suitable for model development and deployment.\n", + "3. **Utilize Machine Learning Model Selection Platforms**: Leverage platforms like TensorFlow Forge, Gluon AI, or Azure Machine Learning to easily deploy trained models from various programming languages and integrate them with data pipelines.\n", + "4. **Implement Interactive Data Storytelling Dashboards**: Develop interactive dashboards that allow business stakeholders to explore predictive analytics insights, drill down into detailed reports, and visualize the impact of their decisions on key metrics.\n", + "5. **Develop a Governance Framework for Model Deployment**: Establish clear policies and procedures for model evaluation, monitoring, and retraining, ensuring continuous improvement and scalability.\n", + "6. **Train Key Stakeholders in Data Science and Predictive Analytics**: Provide targeted training and education programs to develop skills in data science, predictive analytics, and domain expertise, enabling stakeholders to effectively communicate insights and drive decision-making.\n", + "7. **Continuous Feedback Mechanism for Model Improvements**: Establish a continuous feedback loop by incorporating user input, performance metrics, and real-time monitoring into the development process, ensuring high-quality models that meet business needs.\n", + "\n", + "**Implementation Roadmap:**\n", + "\n", + "* Months 1-3: Data Integration Framework Development, Business-Defined Data Pipelines Creation\n", + "* Months 4-6: Machine Learning Model Selection Platforms Deployment, Model Testing & Evaluation\n", + "* Months 7-9: Launch Data Storytelling Dashboards, Governance Framework Development\n", + "* Months 10-12: Stakeholder Onboarding Program, Continuous Feedback Loop Establishment\n" + ] + } + ], + "source": [ + "messages = [{\"role\": \"user\", \"content\": \"Present a solution to the pain point of \" + pain_point + \". Respond only with the solution.\"}]\n", + "response = openai.chat.completions.create(\n", + " model=MODEL,\n", + " messages=messages\n", + ")\n", + "solution = response.choices[0].message.content\n", + "print(f\"Solution: {solution}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "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.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/community_contributions/openai_chatbot_k/README.md b/community_contributions/openai_chatbot_k/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f79ee2e5c7c73b8fa7ebb5f34d7cd3d20d254608 --- /dev/null +++ b/community_contributions/openai_chatbot_k/README.md @@ -0,0 +1,38 @@ +### Setup environment variables +--- + +```md +OPENAI_API_KEY= +PUSHOVER_USER= +PUSHOVER_TOKEN= +RATELIMIT_API="https://ratelimiter-api.ksoftdev.site/api/v1/counter/fixed-window" +REQUEST_TOKEN= +``` + +### Installation +1. Clone the repo +--- +```cmd +git clone httsp://github.com/ken-027/agents.git +``` + +2. Create and set a virtual environment +--- +```cmd +python -m venv agent +agent\Scripts\activate +``` + +3. Install dependencies +--- +```cmd +pip install -r requirements.txt +``` + +4. Run the app +--- +```cmd +cd 1_foundations/community_contributions/openai_chatbot_k && py app.py +or +py 1_foundations/community_contributions/openai_chatbot_k/app.py +``` diff --git a/community_contributions/openai_chatbot_k/app.py b/community_contributions/openai_chatbot_k/app.py new file mode 100644 index 0000000000000000000000000000000000000000..2fc0f68a87f1e98da9a118c9a2a2af93263a2b0d --- /dev/null +++ b/community_contributions/openai_chatbot_k/app.py @@ -0,0 +1,7 @@ +import gradio as gr +import requests +from chatbot import Chatbot + +chatbot = Chatbot() + +gr.ChatInterface(chatbot.chat, type="messages").launch() diff --git a/community_contributions/openai_chatbot_k/chatbot.py b/community_contributions/openai_chatbot_k/chatbot.py new file mode 100644 index 0000000000000000000000000000000000000000..efcca29c9b64e5ffe9efe5161c291e76afa42138 --- /dev/null +++ b/community_contributions/openai_chatbot_k/chatbot.py @@ -0,0 +1,156 @@ +# import all related modules +from openai import OpenAI +import json +from pypdf import PdfReader +from environment import api_key, ai_model, resume_file, summary_file, name, ratelimit_api, request_token +from pushover import Pushover +import requests +from exception import RateLimitError + + +class Chatbot: + __openai = OpenAI(api_key=api_key) + + # define tools setup for OpenAI + def __tools(self): + details_tools_define = { + "user_details": { + "name": "record_user_details", + "description": "Usee this tool to record that a user is interested in being touch and provided an email address", + "parameters": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Email address of this user" + }, + "name": { + "type": "string", + "description": "Name of this user, if they provided" + }, + "notes": { + "type": "string", + "description": "Any additional information about the conversation that's worth recording to give context" + } + }, + "required": ["email"], + "additionalProperties": False + } + }, + "unknown_question": { + "name": "record_unknown_question", + "description": "Always use this tool to record any question that couldn't answered as you didn't know the answer", + "parameters": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "The question that couldn't be answered" + } + }, + "required": ["question"], + "additionalProperties": False + } + } + } + + return [{"type": "function", "function": details_tools_define["user_details"]}, {"type": "function", "function": details_tools_define["unknown_question"]}] + + # handle calling of tools + def __handle_tool_calls(self, tool_calls): + results = [] + for tool_call in tool_calls: + tool_name = tool_call.function.name + arguments = json.loads(tool_call.function.arguments) + print(f"Tool called: {tool_name}", flush=True) + + pushover = Pushover() + + tool = getattr(pushover, tool_name, None) + # tool = globals().get(tool_name) + result = tool(**arguments) if tool else {} + results.append({"role": "tool", "content": json.dumps(result), "tool_call_id": tool_call.id}) + + return results + + + + # read pdf document for the resume + def __get_summary_by_resume(self): + reader = PdfReader(resume_file) + linkedin = "" + for page in reader.pages: + text = page.extract_text() + if text: + linkedin += text + + with open(summary_file, "r", encoding="utf-8") as f: + summary = f.read() + + return {"summary": summary, "linkedin": linkedin} + + + def __get_prompts(self): + loaded_resume = self.__get_summary_by_resume() + summary = loaded_resume["summary"] + linkedin = loaded_resume["linkedin"] + + # setting the prompts + system_prompt = f"You are acting as {name}. You are answering question on {name}'s website, particularly question related to {name}'s career, background, skills and experiences." \ + f"You responsibility is to represent {name} for interactions on the website as faithfully as possible." \ + f"You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions." \ + "Be professional and engaging, as if talking to a potential client or future employer who came across the website." \ + "If you don't know the answer to any question, use your record_unknown_question tool to record the question that you couldn't answer, even if it's about something trivial or unrelated to career." \ + "If the user is engaging in discussion, try to steer them towards getting in touch via email; ask for their email and record it using your record_user_details tool." \ + f"\n\n## Summary:\n{summary}\n\n## LinkedIn Profile:\n{linkedin}\n\n" \ + f"With this context, please chat with the user, always staying in character as {name}." + + return system_prompt + + # chatbot function + def chat(self, message, history): + try: + # implementation of ratelimiter here + response = requests.post( + ratelimit_api, + json={"token": request_token} + ) + status_code = response.status_code + + if (status_code == 429): + raise RateLimitError() + + elif (status_code != 201): + raise Exception(f"Unexpected status code from rate limiter: {status_code}") + + system_prompt = self.__get_prompts() + tools = self.__tools(); + + messages = [] + messages.append({"role": "system", "content": system_prompt}) + messages.extend(history) + messages.append({"role": "user", "content": message}) + + done = False + + while not done: + response = self.__openai.chat.completions.create(model=ai_model, messages=messages, tools=tools) + + finish_reason = response.choices[0].finish_reason + + if finish_reason == "tool_calls": + message = response.choices[0].message + tool_calls = message.tool_calls + results = self.__handle_tool_calls(tool_calls=tool_calls) + messages.append(message) + messages.extend(results) + else: + done = True + + return response.choices[0].message.content + except RateLimitError as rle: + return rle.message + + except Exception as e: + print(f"Error: {e}") + return f"Something went wrong! {e}" diff --git a/community_contributions/openai_chatbot_k/environment.py b/community_contributions/openai_chatbot_k/environment.py new file mode 100644 index 0000000000000000000000000000000000000000..598c93fea45f1a47046b1a4d81b927206c5ea555 --- /dev/null +++ b/community_contributions/openai_chatbot_k/environment.py @@ -0,0 +1,17 @@ +from dotenv import load_dotenv +import os + +load_dotenv(override=True) + + +pushover_user = os.getenv('PUSHOVER_USER') +pushover_token = os.getenv('PUSHOVER_TOKEN') +api_key = os.getenv("OPENAI_API_KEY") +ratelimit_api = os.getenv("RATELIMIT_API") +request_token = os.getenv("REQUEST_TOKEN") + +ai_model = "gpt-4o-mini" +resume_file = "./me/software-developer.pdf" +summary_file = "./me/summary.txt" + +name = "Kenneth Andales" diff --git a/community_contributions/openai_chatbot_k/exception.py b/community_contributions/openai_chatbot_k/exception.py new file mode 100644 index 0000000000000000000000000000000000000000..7ade4d4fb74a773c0685bd7909d053f61f9cc440 --- /dev/null +++ b/community_contributions/openai_chatbot_k/exception.py @@ -0,0 +1,3 @@ +class RateLimitError(Exception): + def __init__(self, message="Too many requests! Please try again tomorrow.") -> None: + self.message = message diff --git a/community_contributions/openai_chatbot_k/me/software-developer.pdf b/community_contributions/openai_chatbot_k/me/software-developer.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f79101cfe199acbda62a2689fab73770822ccd51 Binary files /dev/null and b/community_contributions/openai_chatbot_k/me/software-developer.pdf differ diff --git a/community_contributions/openai_chatbot_k/me/summary.txt b/community_contributions/openai_chatbot_k/me/summary.txt new file mode 100644 index 0000000000000000000000000000000000000000..6617cddf643dc9d7a7c1168ac3c1d50eaa538769 --- /dev/null +++ b/community_contributions/openai_chatbot_k/me/summary.txt @@ -0,0 +1 @@ +My name is Kenneth Andales, I'm a software developer based on the philippines. I love all reading books, playing mobile games, watching anime and nba games, and also playing basketball. diff --git a/community_contributions/openai_chatbot_k/pushover.py b/community_contributions/openai_chatbot_k/pushover.py new file mode 100644 index 0000000000000000000000000000000000000000..49bee5bfc005a75eadab2e1b8cef3eb2bf84c34f --- /dev/null +++ b/community_contributions/openai_chatbot_k/pushover.py @@ -0,0 +1,22 @@ +from environment import pushover_token, pushover_user +import requests + +pushover_url = "https://api.pushover.net/1/messages.json" + +class Pushover: + # notify via pushover + def __push(self, message): + print(f"Push: {message}") + payload = {"user": pushover_user, "token": pushover_token, "message": message} + requests.post(pushover_url, data=payload) + + # tools to notify when user is exist on a prompt + def record_user_details(self, email, name="Anonymous", notes="not provided"): + self.__push(f"Recorded interest from {name} with email {email} and notes {notes}") + return {"status": "ok"} + + + # tools to notify when user not exist on a prompt + def record_unknown_question(self, question): + self.__push(f"Recorded '{question}' that couldn't answered") + return {"status": "ok"} diff --git a/community_contributions/openai_chatbot_k/requirements.txt b/community_contributions/openai_chatbot_k/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..e744d178e2c3e37b9e68d3234727e8ee933984d7 --- /dev/null +++ b/community_contributions/openai_chatbot_k/requirements.txt @@ -0,0 +1,5 @@ +requests +python-dotenv +gradio +pypdf +openai diff --git a/community_contributions/travel_planner_multicall_and_sythesizer.ipynb b/community_contributions/travel_planner_multicall_and_sythesizer.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a2387ece8e6e1f21d6d70da9e1f6ba3973410874 --- /dev/null +++ b/community_contributions/travel_planner_multicall_and_sythesizer.ipynb @@ -0,0 +1,287 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Start with imports - ask ChatGPT to explain any package that you don't know\n", + "\n", + "import os\n", + "import json\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "from anthropic import Anthropic\n", + "from IPython.display import Markdown, display" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load and check your API keys\n", + "
\n", + "- - - - - - - - - - - - - - - -" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Always remember to do this!\n", + "load_dotenv(override=True)\n", + "\n", + "# Function to check and display API key status\n", + "def check_api_key(key_name):\n", + " key = os.getenv(key_name)\n", + " \n", + " if key:\n", + " # Always show the first 7 characters of the key\n", + " print(f\"✓ {key_name} API Key exists and begins... ({key[:7]})\")\n", + " return True\n", + " else:\n", + " print(f\"⚠️ {key_name} API Key not set\")\n", + " return False\n", + "\n", + "# Check each API key (the function now returns True or False)\n", + "has_openai = check_api_key('OPENAI_API_KEY')\n", + "has_anthropic = check_api_key('ANTHROPIC_API_KEY')\n", + "has_google = check_api_key('GOOGLE_API_KEY')\n", + "has_deepseek = check_api_key('DEEPSEEK_API_KEY')\n", + "has_groq = check_api_key('GROQ_API_KEY')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "html" + } + }, + "source": [ + "Input for travel planner
\n", + "Describe yourself, your travel companions, and the destination you plan to visit.\n", + "
\n", + "- - - - - - - - - - - - - - - -" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Provide a description of you or your family. Age, interests, etc.\n", + "person_description = \"family with a 3 year-old\"\n", + "# Provide the name of the specific destination or attraction and country\n", + "destination = \"Belgium, Brussels\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- - - - - - - - - - - - - - - -" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "prompt = f\"\"\"\n", + "Given the following description of a person or family:\n", + "{person_description}\n", + "\n", + "And the requested travel destination or attraction:\n", + "{destination}\n", + "\n", + "Provide a concise response including:\n", + "\n", + "1. Fit rating (1-10) specifically for this person or family.\n", + "2. One compelling positive reason why this destination suits them.\n", + "3. One notable drawback they should consider before visiting.\n", + "4. One important additional aspect to consider related to this location.\n", + "5. Suggest a few additional places that might also be of interest to them that are very close to the destination.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def run_prompt_on_available_models(prompt):\n", + " \"\"\"\n", + " Run a prompt on all available AI models based on API keys.\n", + " Continues processing even if some models fail.\n", + " \"\"\"\n", + " results = {}\n", + " api_response = [{\"role\": \"user\", \"content\": prompt}]\n", + " \n", + " # OpenAI\n", + " if check_api_key('OPENAI_API_KEY'):\n", + " try:\n", + " model_name = \"gpt-4o-mini\"\n", + " openai_client = OpenAI()\n", + " response = openai_client.chat.completions.create(model=model_name, messages=api_response)\n", + " results[model_name] = response.choices[0].message.content\n", + " print(f\"✓ Got response from {model_name}\")\n", + " except Exception as e:\n", + " print(f\"⚠️ Error with {model_name}: {str(e)}\")\n", + " # Continue with other models\n", + " \n", + " # Anthropic\n", + " if check_api_key('ANTHROPIC_API_KEY'):\n", + " try:\n", + " model_name = \"claude-3-7-sonnet-latest\"\n", + " # Create new client each time\n", + " claude = Anthropic()\n", + " \n", + " # Use messages directly \n", + " response = claude.messages.create(\n", + " model=model_name,\n", + " messages=[{\"role\": \"user\", \"content\": prompt}],\n", + " max_tokens=1000\n", + " )\n", + " results[model_name] = response.content[0].text\n", + " print(f\"✓ Got response from {model_name}\")\n", + " except Exception as e:\n", + " print(f\"⚠️ Error with {model_name}: {str(e)}\")\n", + " # Continue with other models\n", + " \n", + " # Google\n", + " if check_api_key('GOOGLE_API_KEY'):\n", + " try:\n", + " model_name = \"gemini-2.0-flash\"\n", + " google_api_key = os.getenv('GOOGLE_API_KEY')\n", + " gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n", + " response = gemini.chat.completions.create(model=model_name, messages=api_response)\n", + " results[model_name] = response.choices[0].message.content\n", + " print(f\"✓ Got response from {model_name}\")\n", + " except Exception as e:\n", + " print(f\"⚠️ Error with {model_name}: {str(e)}\")\n", + " # Continue with other models\n", + " \n", + " # DeepSeek\n", + " if check_api_key('DEEPSEEK_API_KEY'):\n", + " try:\n", + " model_name = \"deepseek-chat\"\n", + " deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n", + " deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n", + " response = deepseek.chat.completions.create(model=model_name, messages=api_response)\n", + " results[model_name] = response.choices[0].message.content\n", + " print(f\"✓ Got response from {model_name}\")\n", + " except Exception as e:\n", + " print(f\"⚠️ Error with {model_name}: {str(e)}\")\n", + " # Continue with other models\n", + " \n", + " # Groq\n", + " if check_api_key('GROQ_API_KEY'):\n", + " try:\n", + " model_name = \"llama-3.3-70b-versatile\"\n", + " groq_api_key = os.getenv('GROQ_API_KEY')\n", + " groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n", + " response = groq.chat.completions.create(model=model_name, messages=api_response)\n", + " results[model_name] = response.choices[0].message.content\n", + " print(f\"✓ Got response from {model_name}\")\n", + " except Exception as e:\n", + " print(f\"⚠️ Error with {model_name}: {str(e)}\")\n", + " # Continue with other models\n", + " \n", + " # Check if we got any responses\n", + " if not results:\n", + " print(\"⚠️ No models were able to provide a response\")\n", + " \n", + " return results\n", + "\n", + "# Get responses from all available models\n", + "model_responses = run_prompt_on_available_models(prompt)\n", + "\n", + "# Display the results\n", + "for model, answer in model_responses.items():\n", + " display(Markdown(f\"## Response from {model}\\n\\n{answer}\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sythesize answers from all models into one\n", + "
\n", + "- - - - - - - - - - - - - - - -" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a synthesis prompt\n", + "synthesis_prompt = f\"\"\"\n", + "Here are the responses from different models:\n", + "\"\"\"\n", + "\n", + "# Add each model's response to the synthesis prompt without mentioning model names\n", + "for index, (model, response) in enumerate(model_responses.items()):\n", + " synthesis_prompt += f\"\\n--- Response {index+1} ---\\n{response}\\n\"\n", + "\n", + "synthesis_prompt += \"\"\"\n", + "Please synthesize these responses into one comprehensive answer that:\n", + "1. Captures the best insights from each response\n", + "2. Resolves any contradictions between responses\n", + "3. Presents a clear and coherent final answer\n", + "4. Maintains the same format as the original responses (numbered list format)\n", + "5.Compiles all additional places mentioned by all models \n", + "\n", + "Your synthesized response:\n", + "\"\"\"\n", + "\n", + "# Create the synthesis\n", + "if check_api_key('OPENAI_API_KEY'):\n", + " try:\n", + " openai_client = OpenAI()\n", + " synthesis_response = openai_client.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=[{\"role\": \"user\", \"content\": synthesis_prompt}]\n", + " )\n", + " synthesized_answer = synthesis_response.choices[0].message.content\n", + " print(\"✓ Successfully synthesized responses with gpt-4o-mini\")\n", + " \n", + " # Display the synthesized answer\n", + " display(Markdown(\"## Synthesized Answer\\n\\n\" + synthesized_answer))\n", + " except Exception as e:\n", + " print(f\"⚠️ Error synthesizing responses with gpt-4o-mini: {str(e)}\")\n", + "else:\n", + " print(\"⚠️ OpenAI API key not available, cannot synthesize responses\")" + ] + } + ], + "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.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/me/Arnav_Agrawal_Resume_2025.pdf b/me/Arnav_Agrawal_Resume_2025.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f8988d96c88e2d321ddf57a7f419f8684807d83b --- /dev/null +++ b/me/Arnav_Agrawal_Resume_2025.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2eee3312a45386ee7498dbc66e55201d68d78e5a16d29856e04bfce4b68694e +size 105255 diff --git a/me/Profile.pdf b/me/Profile.pdf new file mode 100644 index 0000000000000000000000000000000000000000..71d15c9a9f56c64da9583f4ea3d6910b48165ebd Binary files /dev/null and b/me/Profile.pdf differ diff --git a/me/linkedin.pdf b/me/linkedin.pdf new file mode 100644 index 0000000000000000000000000000000000000000..dfc2cb813496c7dfaae8fa89f04c7c36bfb6cfa8 Binary files /dev/null and b/me/linkedin.pdf differ diff --git a/me/summary.txt b/me/summary.txt new file mode 100644 index 0000000000000000000000000000000000000000..5eccb78f0b5ce8f7b4ef965c523d66c8902ba221 --- /dev/null +++ b/me/summary.txt @@ -0,0 +1,2 @@ +My name is Arnav Agrawal. I'm an data scientist, business analyst. I'm originally from India, but I moved to NYC in 2021. +A highly motivated, diligent, focused and adaptable individual. A Data Science Enthusiast with experience in Data Analytics, Machine Learning, Natural Language Processing and Web Development. I have worked in Financial and Healthcare sector. I have graduated from Stevens Institute of Technology by completing Master’s in Data Science. I would like to connect with different people to know moreabout the industry world and how to make a career in it. \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..c613376861df2c6a5ec75897b43a7014307877c2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +requests +python-dotenv +gradio +pypdf +openai +openai-agents \ No newline at end of file