├── LICENSE ├── README.md ├── gpt4o_mini_fine_tuning.ipynb ├── openai_assistants_files_api_based_rag.ipynb ├── openai_meta_prompt.ipynb ├── pandasai.ipynb └── vanna_sqlite_nba_shots.ipynb /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 sugarforever 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenAI Tutorials 2 | OpenAI tutorials from the basics to advanced 3 | -------------------------------------------------------------------------------- /gpt4o_mini_fine_tuning.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "provenance": [], 7 | "include_colab_link": true 8 | }, 9 | "kernelspec": { 10 | "name": "python3", 11 | "display_name": "Python 3" 12 | }, 13 | "language_info": { 14 | "name": "python" 15 | } 16 | }, 17 | "cells": [ 18 | { 19 | "cell_type": "markdown", 20 | "metadata": { 21 | "id": "view-in-github", 22 | "colab_type": "text" 23 | }, 24 | "source": [ 25 | "\"Open" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "source": [ 31 | "## Set Up Environment" 32 | ], 33 | "metadata": { 34 | "id": "YhwwvuUggrQG" 35 | } 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 1, 40 | "metadata": { 41 | "colab": { 42 | "base_uri": "https://localhost:8080/" 43 | }, 44 | "id": "K6pNBFzcer0X", 45 | "outputId": "9a7c02e0-6167-43aa-ceba-d6aa21b5558b" 46 | }, 47 | "outputs": [ 48 | { 49 | "output_type": "stream", 50 | "name": "stdout", 51 | "text": [ 52 | "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/337.0 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━\u001b[0m \u001b[32m327.7/337.0 kB\u001b[0m \u001b[31m14.6 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\r\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m337.0/337.0 kB\u001b[0m \u001b[31m8.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", 53 | "\u001b[?25h\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/75.6 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.6/75.6 kB\u001b[0m \u001b[31m4.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", 54 | "\u001b[?25h\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/77.9 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m77.9/77.9 kB\u001b[0m \u001b[31m4.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", 55 | "\u001b[?25h\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/58.3 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.3/58.3 kB\u001b[0m \u001b[31m3.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", 56 | "\u001b[?25h" 57 | ] 58 | } 59 | ], 60 | "source": [ 61 | "!pip install --upgrade --quiet openai" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "source": [ 67 | "from google.colab import userdata\n", 68 | "import os\n", 69 | "\n", 70 | "os.environ[\"OPENAI_API_KEY\"] = userdata.get('OPENAI_API_KEY')" 71 | ], 72 | "metadata": { 73 | "id": "kFaWZqnbfN1k" 74 | }, 75 | "execution_count": 2, 76 | "outputs": [] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "source": [ 81 | "import json\n", 82 | "import openai\n", 83 | "import os\n", 84 | "import pandas as pd\n", 85 | "from pprint import pprint\n", 86 | "\n", 87 | "client = openai.OpenAI()" 88 | ], 89 | "metadata": { 90 | "id": "r8r4EUxCfYOE" 91 | }, 92 | "execution_count": 3, 93 | "outputs": [] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "source": [ 98 | "recipe_df = pd.read_csv(\"/content/cookbook_recipes_nlg_10k.csv\")\n", 99 | "\n", 100 | "recipe_df.head()" 101 | ], 102 | "metadata": { 103 | "colab": { 104 | "base_uri": "https://localhost:8080/", 105 | "height": 276 106 | }, 107 | "id": "sIPRcIwdfiMA", 108 | "outputId": "a61025d1-0862-4e48-8ede-df5a3660a93e" 109 | }, 110 | "execution_count": 4, 111 | "outputs": [ 112 | { 113 | "output_type": "execute_result", 114 | "data": { 115 | "text/plain": [ 116 | " title ingredients \\\n", 117 | "0 No-Bake Nut Cookies [\"1 c. firmly packed brown sugar\", \"1/2 c. eva... \n", 118 | "1 Jewell Ball'S Chicken [\"1 small jar chipped beef, cut up\", \"4 boned ... \n", 119 | "2 Creamy Corn [\"2 (16 oz.) pkg. frozen corn\", \"1 (8 oz.) pkg... \n", 120 | "3 Chicken Funny [\"1 large whole chicken\", \"2 (10 1/2 oz.) cans... \n", 121 | "4 Reeses Cups(Candy) [\"1 c. peanut butter\", \"3/4 c. graham cracker ... \n", 122 | "\n", 123 | " directions \\\n", 124 | "0 [\"In a heavy 2-quart saucepan, mix brown sugar... \n", 125 | "1 [\"Place chipped beef on bottom of baking dish.... \n", 126 | "2 [\"In a slow cooker, combine all ingredients. C... \n", 127 | "3 [\"Boil and debone chicken.\", \"Put bite size pi... \n", 128 | "4 [\"Combine first four ingredients and press in ... \n", 129 | "\n", 130 | " link source \\\n", 131 | "0 www.cookbooks.com/Recipe-Details.aspx?id=44874 www.cookbooks.com \n", 132 | "1 www.cookbooks.com/Recipe-Details.aspx?id=699419 www.cookbooks.com \n", 133 | "2 www.cookbooks.com/Recipe-Details.aspx?id=10570 www.cookbooks.com \n", 134 | "3 www.cookbooks.com/Recipe-Details.aspx?id=897570 www.cookbooks.com \n", 135 | "4 www.cookbooks.com/Recipe-Details.aspx?id=659239 www.cookbooks.com \n", 136 | "\n", 137 | " NER \n", 138 | "0 [\"brown sugar\", \"milk\", \"vanilla\", \"nuts\", \"bu... \n", 139 | "1 [\"beef\", \"chicken breasts\", \"cream of mushroom... \n", 140 | "2 [\"frozen corn\", \"cream cheese\", \"butter\", \"gar... \n", 141 | "3 [\"chicken\", \"chicken gravy\", \"cream of mushroo... \n", 142 | "4 [\"peanut butter\", \"graham cracker crumbs\", \"bu... " 143 | ], 144 | "text/html": [ 145 | "\n", 146 | "
\n", 147 | "
\n", 148 | "\n", 161 | "\n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | " \n", 174 | " \n", 175 | " \n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | " \n", 184 | " \n", 185 | " \n", 186 | " \n", 187 | " \n", 188 | " \n", 189 | " \n", 190 | " \n", 191 | " \n", 192 | " \n", 193 | " \n", 194 | " \n", 195 | " \n", 196 | " \n", 197 | " \n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | " \n", 203 | " \n", 204 | " \n", 205 | " \n", 206 | " \n", 207 | " \n", 208 | " \n", 209 | " \n", 210 | " \n", 211 | " \n", 212 | " \n", 213 | " \n", 214 | " \n", 215 | " \n", 216 | " \n", 217 | " \n", 218 | " \n", 219 | " \n", 220 | "
titleingredientsdirectionslinksourceNER
0No-Bake Nut Cookies[\"1 c. firmly packed brown sugar\", \"1/2 c. eva...[\"In a heavy 2-quart saucepan, mix brown sugar...www.cookbooks.com/Recipe-Details.aspx?id=44874www.cookbooks.com[\"brown sugar\", \"milk\", \"vanilla\", \"nuts\", \"bu...
1Jewell Ball'S Chicken[\"1 small jar chipped beef, cut up\", \"4 boned ...[\"Place chipped beef on bottom of baking dish....www.cookbooks.com/Recipe-Details.aspx?id=699419www.cookbooks.com[\"beef\", \"chicken breasts\", \"cream of mushroom...
2Creamy Corn[\"2 (16 oz.) pkg. frozen corn\", \"1 (8 oz.) pkg...[\"In a slow cooker, combine all ingredients. C...www.cookbooks.com/Recipe-Details.aspx?id=10570www.cookbooks.com[\"frozen corn\", \"cream cheese\", \"butter\", \"gar...
3Chicken Funny[\"1 large whole chicken\", \"2 (10 1/2 oz.) cans...[\"Boil and debone chicken.\", \"Put bite size pi...www.cookbooks.com/Recipe-Details.aspx?id=897570www.cookbooks.com[\"chicken\", \"chicken gravy\", \"cream of mushroo...
4Reeses Cups(Candy)[\"1 c. peanut butter\", \"3/4 c. graham cracker ...[\"Combine first four ingredients and press in ...www.cookbooks.com/Recipe-Details.aspx?id=659239www.cookbooks.com[\"peanut butter\", \"graham cracker crumbs\", \"bu...
\n", 221 | "
\n", 222 | "
\n", 223 | "\n", 224 | "
\n", 225 | " \n", 233 | "\n", 234 | " \n", 274 | "\n", 275 | " \n", 299 | "
\n", 300 | "\n", 301 | "\n", 302 | "
\n", 303 | " \n", 314 | "\n", 315 | "\n", 404 | "\n", 405 | " \n", 427 | "
\n", 428 | "\n", 429 | "
\n", 430 | "
\n" 431 | ], 432 | "application/vnd.google.colaboratory.intrinsic+json": { 433 | "type": "dataframe", 434 | "variable_name": "recipe_df", 435 | "summary": "{\n \"name\": \"recipe_df\",\n \"rows\": 10000,\n \"fields\": [\n {\n \"column\": \"title\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 7194,\n \"samples\": [\n \"Strawberry Shake\",\n \"Pumpkin Chiffon Cake\",\n \"Black Bean Salad\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"ingredients\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 9999,\n \"samples\": [\n \"[\\\"2 sticks margarine\\\", \\\"1/2 c. sugar\\\", \\\"graham crackers\\\", \\\"chopped nuts\\\", \\\"Hershey bars\\\"]\",\n \"[\\\"2 c. coarsely chopped pretzels\\\", \\\"3/4 c. melted margarine\\\", \\\"2 Tbsp. sugar\\\", \\\"8 oz. cream cheese, softened\\\", \\\"8 oz. whipped topping\\\", \\\"1/2 c. sugar\\\", \\\"1 large pkg. strawberry jello\\\", \\\"2 c. boiling water\\\", \\\"2 (10 oz.) pkg. frozen strawberries\\\"]\",\n \"[\\\"4 (6 oz.) boneless, skinned chicken breasts\\\", \\\"3 Tbsp. flour\\\", \\\"1 Tbsp. curry powder\\\", \\\"4 Tbsp. oil\\\", \\\"1 Tbsp. sugar\\\", \\\"2 chicken bouillon cubes\\\", \\\"1 c. water\\\", \\\"16 oz. can apricots\\\", \\\"1/2 c. chopped onion\\\", \\\"1 c. sliced mushrooms\\\", \\\"2 Tbsp. soy sauce\\\", \\\"2 Tbsp. lemon juice\\\"]\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"directions\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 9989,\n \"samples\": [\n \"[\\\"Adjust oven rack to lowest position.\\\", \\\"Preheat oven to 350\\\\u00b0. In food processor with steel blade, combine flour, salt and Crisco; pulse about 6 seconds, until mixture resembles coarse crumbs.\\\", \\\"Add milk and process until dough forms a ball.\\\", \\\"Continue processing 2 minutes.\\\", \\\"On floured surface, roll dough into rectangle 1/4-inch thick.\\\", \\\"Fold dough in half, pressing lightly. With a 1 1/2-inch biscuit cutter, cut out dough and place on cookie sheet.\\\", \\\"Re-roll scraps and repeat, cutting more biscuits. Prick each biscuit with a fork.\\\", \\\"Bake 30 to 35 minutes, until pale golden.\\\", \\\"Turn off oven, leave biscuits in oven for 35 minutes. Makes about 2 1/2 dozen.\\\", \\\"Contains 100 calories each.\\\"]\",\n \"[\\\"Cream the margarine, cream cheese and sugar.\\\", \\\"Add eggs and vanilla; beat well.\\\", \\\"Sift together the flour, baking powder, baking soda and salt.\\\", \\\"Add alternately with milk, mixing well after each addition. Pour into a greased and floured 13 x 9-inch pan.\\\", \\\"Sprinkle with crumb topping.\\\", \\\"Bake at 350\\\\u00b0 for 35 to 40 minutes.\\\"]\",\n \"[\\\"Mix in pan the sugar, cornstarch and salt. Stir milk in gradually. Cook, stirring constantly, until mixture boils.\\\", \\\"Remove from heat.\\\", \\\"Stir in vanilla.\\\"]\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"link\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 10000,\n \"samples\": [\n \"www.cookbooks.com/Recipe-Details.aspx?id=1062310\",\n \"www.cookbooks.com/Recipe-Details.aspx?id=217349\",\n \"www.cookbooks.com/Recipe-Details.aspx?id=1018986\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"source\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 1,\n \"samples\": [\n \"www.cookbooks.com\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"NER\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 9953,\n \"samples\": [\n \"[\\\"egg\\\", \\\"celery\\\", \\\"potatoes\\\", \\\"pimentos\\\", \\\"onions\\\", \\\"yogurt\\\", \\\"mustard\\\", \\\"salt\\\"]\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" 436 | } 437 | }, 438 | "metadata": {}, 439 | "execution_count": 4 440 | } 441 | ] 442 | }, 443 | { 444 | "cell_type": "markdown", 445 | "source": [ 446 | "## Prepare Data for Training and Validation" 447 | ], 448 | "metadata": { 449 | "id": "8DVqrVASgj3x" 450 | } 451 | }, 452 | { 453 | "cell_type": "code", 454 | "source": [ 455 | "system_message = \"You are a helpful recipe assistant. You are to extract the generic ingredients from each of the recipes provided.\"\n", 456 | "\n", 457 | "\n", 458 | "def create_user_message(row):\n", 459 | " return f\"Title: {row['title']}\\n\\nIngredients: {row['ingredients']}\\n\\nGeneric ingredients: \"\n", 460 | "\n", 461 | "\n", 462 | "def prepare_example_conversation(row):\n", 463 | " return {\n", 464 | " \"messages\": [\n", 465 | " {\"role\": \"system\", \"content\": system_message},\n", 466 | " {\"role\": \"user\", \"content\": create_user_message(row)},\n", 467 | " {\"role\": \"assistant\", \"content\": row[\"NER\"]},\n", 468 | " ]\n", 469 | " }\n", 470 | "\n", 471 | "\n", 472 | "pprint(prepare_example_conversation(recipe_df.iloc[0]))" 473 | ], 474 | "metadata": { 475 | "colab": { 476 | "base_uri": "https://localhost:8080/" 477 | }, 478 | "id": "eQ5VYJ9Gfo-Z", 479 | "outputId": "6f310ab9-79e3-4917-d5fc-0c03213b11d1" 480 | }, 481 | "execution_count": 5, 482 | "outputs": [ 483 | { 484 | "output_type": "stream", 485 | "name": "stdout", 486 | "text": [ 487 | "{'messages': [{'content': 'You are a helpful recipe assistant. You are to '\n", 488 | " 'extract the generic ingredients from each of the '\n", 489 | " 'recipes provided.',\n", 490 | " 'role': 'system'},\n", 491 | " {'content': 'Title: No-Bake Nut Cookies\\n'\n", 492 | " '\\n'\n", 493 | " 'Ingredients: [\"1 c. firmly packed brown sugar\", '\n", 494 | " '\"1/2 c. evaporated milk\", \"1/2 tsp. vanilla\", \"1/2 '\n", 495 | " 'c. broken nuts (pecans)\", \"2 Tbsp. butter or '\n", 496 | " 'margarine\", \"3 1/2 c. bite size shredded rice '\n", 497 | " 'biscuits\"]\\n'\n", 498 | " '\\n'\n", 499 | " 'Generic ingredients: ',\n", 500 | " 'role': 'user'},\n", 501 | " {'content': '[\"brown sugar\", \"milk\", \"vanilla\", \"nuts\", '\n", 502 | " '\"butter\", \"bite size shredded rice biscuits\"]',\n", 503 | " 'role': 'assistant'}]}\n" 504 | ] 505 | } 506 | ] 507 | }, 508 | { 509 | "cell_type": "code", 510 | "source": [ 511 | "# use the first 100 rows of the dataset for training\n", 512 | "training_df = recipe_df.loc[0:100]\n", 513 | "\n", 514 | "# apply the prepare_example_conversation function to each row of the training_df\n", 515 | "training_data = training_df.apply(prepare_example_conversation, axis=1).tolist()\n", 516 | "\n", 517 | "for example in training_data[:5]:\n", 518 | " print(example)" 519 | ], 520 | "metadata": { 521 | "colab": { 522 | "base_uri": "https://localhost:8080/" 523 | }, 524 | "id": "gcVnV3uIftZ3", 525 | "outputId": "86c4deed-e62d-4243-c170-b75344a84377" 526 | }, 527 | "execution_count": 6, 528 | "outputs": [ 529 | { 530 | "output_type": "stream", 531 | "name": "stdout", 532 | "text": [ 533 | "{'messages': [{'role': 'system', 'content': 'You are a helpful recipe assistant. You are to extract the generic ingredients from each of the recipes provided.'}, {'role': 'user', 'content': 'Title: No-Bake Nut Cookies\\n\\nIngredients: [\"1 c. firmly packed brown sugar\", \"1/2 c. evaporated milk\", \"1/2 tsp. vanilla\", \"1/2 c. broken nuts (pecans)\", \"2 Tbsp. butter or margarine\", \"3 1/2 c. bite size shredded rice biscuits\"]\\n\\nGeneric ingredients: '}, {'role': 'assistant', 'content': '[\"brown sugar\", \"milk\", \"vanilla\", \"nuts\", \"butter\", \"bite size shredded rice biscuits\"]'}]}\n", 534 | "{'messages': [{'role': 'system', 'content': 'You are a helpful recipe assistant. You are to extract the generic ingredients from each of the recipes provided.'}, {'role': 'user', 'content': 'Title: Jewell Ball\\'S Chicken\\n\\nIngredients: [\"1 small jar chipped beef, cut up\", \"4 boned chicken breasts\", \"1 can cream of mushroom soup\", \"1 carton sour cream\"]\\n\\nGeneric ingredients: '}, {'role': 'assistant', 'content': '[\"beef\", \"chicken breasts\", \"cream of mushroom soup\", \"sour cream\"]'}]}\n", 535 | "{'messages': [{'role': 'system', 'content': 'You are a helpful recipe assistant. You are to extract the generic ingredients from each of the recipes provided.'}, {'role': 'user', 'content': 'Title: Creamy Corn\\n\\nIngredients: [\"2 (16 oz.) pkg. frozen corn\", \"1 (8 oz.) pkg. cream cheese, cubed\", \"1/3 c. butter, cubed\", \"1/2 tsp. garlic powder\", \"1/2 tsp. salt\", \"1/4 tsp. pepper\"]\\n\\nGeneric ingredients: '}, {'role': 'assistant', 'content': '[\"frozen corn\", \"cream cheese\", \"butter\", \"garlic powder\", \"salt\", \"pepper\"]'}]}\n", 536 | "{'messages': [{'role': 'system', 'content': 'You are a helpful recipe assistant. You are to extract the generic ingredients from each of the recipes provided.'}, {'role': 'user', 'content': 'Title: Chicken Funny\\n\\nIngredients: [\"1 large whole chicken\", \"2 (10 1/2 oz.) cans chicken gravy\", \"1 (10 1/2 oz.) can cream of mushroom soup\", \"1 (6 oz.) box Stove Top stuffing\", \"4 oz. shredded cheese\"]\\n\\nGeneric ingredients: '}, {'role': 'assistant', 'content': '[\"chicken\", \"chicken gravy\", \"cream of mushroom soup\", \"shredded cheese\"]'}]}\n", 537 | "{'messages': [{'role': 'system', 'content': 'You are a helpful recipe assistant. You are to extract the generic ingredients from each of the recipes provided.'}, {'role': 'user', 'content': 'Title: Reeses Cups(Candy) \\n\\nIngredients: [\"1 c. peanut butter\", \"3/4 c. graham cracker crumbs\", \"1 c. melted butter\", \"1 lb. (3 1/2 c.) powdered sugar\", \"1 large pkg. chocolate chips\"]\\n\\nGeneric ingredients: '}, {'role': 'assistant', 'content': '[\"peanut butter\", \"graham cracker crumbs\", \"butter\", \"powdered sugar\", \"chocolate chips\"]'}]}\n" 538 | ] 539 | } 540 | ] 541 | }, 542 | { 543 | "cell_type": "code", 544 | "source": [ 545 | "# use the next 100 rows of the dataset for validation\n", 546 | "validation_df = recipe_df.loc[101:200]\n", 547 | "validation_data = validation_df.apply(\n", 548 | " prepare_example_conversation, axis=1).tolist()" 549 | ], 550 | "metadata": { 551 | "id": "zKYZ1RIDfyx5" 552 | }, 553 | "execution_count": 7, 554 | "outputs": [] 555 | }, 556 | { 557 | "cell_type": "code", 558 | "source": [ 559 | "def write_jsonl(data_list: list, filename: str) -> None:\n", 560 | " with open(filename, \"w\") as out:\n", 561 | " for ddict in data_list:\n", 562 | " jout = json.dumps(ddict) + \"\\n\"\n", 563 | " out.write(jout)" 564 | ], 565 | "metadata": { 566 | "id": "NFiNSbUjf6Co" 567 | }, 568 | "execution_count": 8, 569 | "outputs": [] 570 | }, 571 | { 572 | "cell_type": "code", 573 | "source": [ 574 | "training_file_name = \"/content/tmp_recipe_finetune_training.jsonl\"\n", 575 | "write_jsonl(training_data, training_file_name)\n", 576 | "\n", 577 | "validation_file_name = \"/content/tmp_recipe_finetune_validation.jsonl\"\n", 578 | "write_jsonl(validation_data, validation_file_name)" 579 | ], 580 | "metadata": { 581 | "id": "UWvQ2b-Mf7Nx" 582 | }, 583 | "execution_count": 9, 584 | "outputs": [] 585 | }, 586 | { 587 | "cell_type": "code", 588 | "source": [ 589 | "!head -n 5 tmp_recipe_finetune_training.jsonl" 590 | ], 591 | "metadata": { 592 | "id": "DiuGHChuf-5V" 593 | }, 594 | "execution_count": null, 595 | "outputs": [] 596 | }, 597 | { 598 | "cell_type": "markdown", 599 | "source": [ 600 | "## Upload Files" 601 | ], 602 | "metadata": { 603 | "id": "iSyJgXIPgeJu" 604 | } 605 | }, 606 | { 607 | "cell_type": "code", 608 | "source": [ 609 | "def upload_file(file_name: str, purpose: str) -> str:\n", 610 | " with open(file_name, \"rb\") as file_fd:\n", 611 | " response = client.files.create(file=file_fd, purpose=purpose)\n", 612 | " return response.id\n", 613 | "\n", 614 | "\n", 615 | "training_file_id = upload_file(training_file_name, \"fine-tune\")\n", 616 | "validation_file_id = upload_file(validation_file_name, \"fine-tune\")\n", 617 | "\n", 618 | "print(\"Training file ID:\", training_file_id)\n", 619 | "print(\"Validation file ID:\", validation_file_id)" 620 | ], 621 | "metadata": { 622 | "colab": { 623 | "base_uri": "https://localhost:8080/" 624 | }, 625 | "id": "8uwvsIFmgEsc", 626 | "outputId": "9a20bbfe-0457-4b7b-c7a9-75fb395dc034" 627 | }, 628 | "execution_count": 10, 629 | "outputs": [ 630 | { 631 | "output_type": "stream", 632 | "name": "stdout", 633 | "text": [ 634 | "Training file ID: file-53ZWTASi31k2KsHDXW2V7g1r\n", 635 | "Validation file ID: file-lmgs8Dj0RRHMefel5zrvuGqg\n" 636 | ] 637 | } 638 | ] 639 | }, 640 | { 641 | "cell_type": "markdown", 642 | "source": [ 643 | "## Create Fine-tuning Job" 644 | ], 645 | "metadata": { 646 | "id": "pymxCo57gXjV" 647 | } 648 | }, 649 | { 650 | "cell_type": "code", 651 | "source": [ 652 | "MODEL = \"gpt-4o-mini-2024-07-18\"\n", 653 | "\n", 654 | "response = client.fine_tuning.jobs.create(\n", 655 | " training_file=training_file_id,\n", 656 | " validation_file=validation_file_id,\n", 657 | " model=MODEL,\n", 658 | " suffix=\"recipe-ner\",\n", 659 | ")\n", 660 | "\n", 661 | "job_id = response.id\n", 662 | "\n", 663 | "print(\"Job ID:\", response.id)\n", 664 | "print(\"Status:\", response.status)" 665 | ], 666 | "metadata": { 667 | "colab": { 668 | "base_uri": "https://localhost:8080/" 669 | }, 670 | "id": "GXsgdvG7gQ72", 671 | "outputId": "0d8c9c0c-ed1a-417e-ec61-0764d512765f" 672 | }, 673 | "execution_count": 11, 674 | "outputs": [ 675 | { 676 | "output_type": "stream", 677 | "name": "stdout", 678 | "text": [ 679 | "Job ID: ftjob-zBtDll3AazYg1imIoEC9klma\n", 680 | "Status: validating_files\n" 681 | ] 682 | } 683 | ] 684 | }, 685 | { 686 | "cell_type": "markdown", 687 | "source": [ 688 | "## Check Job Status" 689 | ], 690 | "metadata": { 691 | "id": "HUWt_hHbgT_B" 692 | } 693 | }, 694 | { 695 | "cell_type": "code", 696 | "source": [ 697 | "response = client.fine_tuning.jobs.retrieve(job_id)\n", 698 | "\n", 699 | "print(\"Job ID:\", response.id)\n", 700 | "print(\"Status:\", response.status)\n", 701 | "print(\"Trained Tokens:\", response.trained_tokens)" 702 | ], 703 | "metadata": { 704 | "colab": { 705 | "base_uri": "https://localhost:8080/" 706 | }, 707 | "id": "2YUWKFxqgS0z", 708 | "outputId": "0cff7641-fafb-4db0-8949-cef572a57362" 709 | }, 710 | "execution_count": 12, 711 | "outputs": [ 712 | { 713 | "output_type": "stream", 714 | "name": "stdout", 715 | "text": [ 716 | "Job ID: ftjob-zBtDll3AazYg1imIoEC9klma\n", 717 | "Status: validating_files\n", 718 | "Trained Tokens: None\n" 719 | ] 720 | } 721 | ] 722 | }, 723 | { 724 | "cell_type": "code", 725 | "source": [ 726 | "response = client.fine_tuning.jobs.list_events(job_id)\n", 727 | "\n", 728 | "events = response.data\n", 729 | "events.reverse()\n", 730 | "\n", 731 | "for event in events:\n", 732 | " print(event.message)" 733 | ], 734 | "metadata": { 735 | "colab": { 736 | "base_uri": "https://localhost:8080/" 737 | }, 738 | "id": "cQIxf1PPh_lq", 739 | "outputId": "6565dc96-61af-4a3f-c74d-484ffb9a5d1f" 740 | }, 741 | "execution_count": 29, 742 | "outputs": [ 743 | { 744 | "output_type": "stream", 745 | "name": "stdout", 746 | "text": [ 747 | "Step 288/303: training loss=0.00\n", 748 | "Step 289/303: training loss=0.00\n", 749 | "Step 290/303: training loss=0.00, validation loss=0.27\n", 750 | "Step 291/303: training loss=0.35\n", 751 | "Step 292/303: training loss=0.00\n", 752 | "Step 293/303: training loss=0.20\n", 753 | "Step 294/303: training loss=0.00\n", 754 | "Step 295/303: training loss=0.00\n", 755 | "Step 296/303: training loss=0.00\n", 756 | "Step 297/303: training loss=2.24\n", 757 | "Step 298/303: training loss=0.00\n", 758 | "Step 299/303: training loss=0.00\n", 759 | "Step 300/303: training loss=0.00, validation loss=0.06\n", 760 | "Step 301/303: training loss=0.00\n", 761 | "Step 302/303: training loss=0.00\n", 762 | "Step 303/303: training loss=0.00, full validation loss=0.23\n", 763 | "Checkpoint created at step 101 with Snapshot ID: ft:gpt-4o-mini-2024-07-18:personal:recipe-ner:9oeQ8SOF:ckpt-step-101\n", 764 | "Checkpoint created at step 202 with Snapshot ID: ft:gpt-4o-mini-2024-07-18:personal:recipe-ner:9oeQ8gZp:ckpt-step-202\n", 765 | "New fine-tuned model created: ft:gpt-4o-mini-2024-07-18:personal:recipe-ner:9oeQ9EMt\n", 766 | "The job has successfully completed\n" 767 | ] 768 | } 769 | ] 770 | }, 771 | { 772 | "cell_type": "markdown", 773 | "source": [ 774 | "### Get Fine-tuned Model ID" 775 | ], 776 | "metadata": { 777 | "id": "ch6-5z2liKam" 778 | } 779 | }, 780 | { 781 | "cell_type": "code", 782 | "source": [ 783 | "response = client.fine_tuning.jobs.retrieve(job_id)\n", 784 | "fine_tuned_model_id = response.fine_tuned_model\n", 785 | "\n", 786 | "if fine_tuned_model_id is None:\n", 787 | " raise RuntimeError(\n", 788 | " \"Fine-tuned model ID not found. Your job has likely not been completed yet.\"\n", 789 | " )\n", 790 | "\n", 791 | "print(\"Fine-tuned model ID:\", fine_tuned_model_id)" 792 | ], 793 | "metadata": { 794 | "colab": { 795 | "base_uri": "https://localhost:8080/" 796 | }, 797 | "id": "MhoO3LmTiJv3", 798 | "outputId": "0dffd9d1-baeb-4b8f-99ea-1a0173492fde" 799 | }, 800 | "execution_count": 30, 801 | "outputs": [ 802 | { 803 | "output_type": "stream", 804 | "name": "stdout", 805 | "text": [ 806 | "Fine-tuned model ID: ft:gpt-4o-mini-2024-07-18:personal:recipe-ner:9oeQ9EMt\n" 807 | ] 808 | } 809 | ] 810 | }, 811 | { 812 | "cell_type": "markdown", 813 | "source": [ 814 | "## Inference" 815 | ], 816 | "metadata": { 817 | "id": "E9gWkoGiiebX" 818 | } 819 | }, 820 | { 821 | "cell_type": "code", 822 | "source": [ 823 | "test_df = recipe_df.loc[201:300]\n", 824 | "test_row = test_df.iloc[0]\n", 825 | "test_messages = []\n", 826 | "test_messages.append({\"role\": \"system\", \"content\": system_message})\n", 827 | "user_message = create_user_message(test_row)\n", 828 | "test_messages.append({\"role\": \"user\", \"content\": user_message})\n", 829 | "\n", 830 | "pprint(test_messages)" 831 | ], 832 | "metadata": { 833 | "colab": { 834 | "base_uri": "https://localhost:8080/" 835 | }, 836 | "id": "gRtsiGO9idye", 837 | "outputId": "0b8d5bff-866b-4b96-e12d-cc4af2e985ab" 838 | }, 839 | "execution_count": 31, 840 | "outputs": [ 841 | { 842 | "output_type": "stream", 843 | "name": "stdout", 844 | "text": [ 845 | "[{'content': 'You are a helpful recipe assistant. You are to extract the '\n", 846 | " 'generic ingredients from each of the recipes provided.',\n", 847 | " 'role': 'system'},\n", 848 | " {'content': 'Title: Beef Brisket\\n'\n", 849 | " '\\n'\n", 850 | " 'Ingredients: [\"4 lb. beef brisket\", \"1 c. catsup\", \"1 c. water\", '\n", 851 | " '\"1/2 onion, minced\", \"2 Tbsp. cider vinegar\", \"1 Tbsp. prepared '\n", 852 | " 'horseradish\", \"1 Tbsp. prepared mustard\", \"1 tsp. salt\", \"1/2 '\n", 853 | " 'tsp. pepper\"]\\n'\n", 854 | " '\\n'\n", 855 | " 'Generic ingredients: ',\n", 856 | " 'role': 'user'}]\n" 857 | ] 858 | } 859 | ] 860 | }, 861 | { 862 | "cell_type": "code", 863 | "source": [ 864 | "response = client.chat.completions.create(\n", 865 | " model=fine_tuned_model_id, messages=test_messages, temperature=0, max_tokens=500\n", 866 | ")\n", 867 | "print(response.choices[0].message.content)" 868 | ], 869 | "metadata": { 870 | "colab": { 871 | "base_uri": "https://localhost:8080/" 872 | }, 873 | "id": "ZDVLfPLgilKL", 874 | "outputId": "272e3710-5187-4073-f0fd-f0e9c52079a5" 875 | }, 876 | "execution_count": 32, 877 | "outputs": [ 878 | { 879 | "output_type": "stream", 880 | "name": "stdout", 881 | "text": [ 882 | "[\"beef brisket\", \"catsup\", \"water\", \"onion\", \"cider vinegar\", \"horseradish\", \"mustard\", \"salt\", \"pepper\"]\n" 883 | ] 884 | } 885 | ] 886 | } 887 | ] 888 | } -------------------------------------------------------------------------------- /openai_assistants_files_api_based_rag.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "provenance": [], 7 | "authorship_tag": "ABX9TyO5q+vGSTUY3/WT0PlvMgBd", 8 | "include_colab_link": true 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | }, 14 | "language_info": { 15 | "name": "python" 16 | } 17 | }, 18 | "cells": [ 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "id": "view-in-github", 23 | "colab_type": "text" 24 | }, 25 | "source": [ 26 | "\"Open" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "source": [ 32 | "# OpenAI Assistants and Files API based RAG Application\n", 33 | "\n", 34 | "**Assistants API** allows developers to build AI assistants that can handle the stateful operations required for LLM based applications, including persistent threads and messages, files, and automatic RAG.\n", 35 | "\n", 36 | "An Assistant has instructions and can leverage models, tools, and knowledge to respond to user queries. The Assistants API currently supports three types of tools:\n", 37 | "\n", 38 | "- Code Interpreter\n", 39 | "- Retrieval\n", 40 | "- Function calling\n", 41 | "\n", 42 | "Notice that, when we give `Assistants` access to OpenAI-hosted tools listed above, the usage of the tools comes at an additional fee.\n", 43 | "\n", 44 | "**RAG** (Retrieval Augmented Generation) is a technique used in natural language processing that employes the capabilities of retrieval-based models and generative models to improve the quality and relevance of generated text.\n", 45 | "\n", 46 | "Document based QA bot is a classic use case of RAG. The mainstream LLM frameworks for example `LangChain` and `LlamaIndex` support building such RAG application.\n", 47 | "\n", 48 | "In this tutorial, I will show you how to develop a RAG application with OpenAI `Assistants` and `Files` API. The code may be cleaner, the solution may be more elegant, and it may cost a bit more, considering the extra charge for using OpenAI hosted tool - **`Retrieval`**.\n", 49 | "\n", 50 | "With this solution, you don't have to deal with the following tedious operations:\n", 51 | "\n", 52 | "- Split text with proper strategy\n", 53 | "- Vectorize text chunks\n", 54 | "- Persist vector data set\n", 55 | "- Similarity search\n" 56 | ], 57 | "metadata": { 58 | "id": "Dej4rmcv1qCm" 59 | } 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "source": [ 64 | "## Prepare the environment\n", 65 | "\n", 66 | "In order to run the example code below, make sure you have a valid OpenAI API key with access to the model `gpt-4-1106-preview`.\n", 67 | "\n", 68 | "You also should have the `.env` file ready in current directory with the content in the pattern below:\n", 69 | "\n", 70 | "```shell\n", 71 | "OPENAI_API_KEY=sk-xxxxxx\n", 72 | "```\n", 73 | "\n", 74 | "Now let's install the necessary Python packages." 75 | ], 76 | "metadata": { 77 | "id": "ipQHI0Fe7OP7" 78 | } 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 105, 83 | "metadata": { 84 | "id": "GkF_TXMN2AML" 85 | }, 86 | "outputs": [], 87 | "source": [ 88 | "!pip install openai python-dotenv -U -q" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "source": [ 94 | "## Coding Time\n", 95 | "\n", 96 | "We can start coding. In this example, we will use the following PDF file as the knowledge base:\n", 97 | "\n", 98 | "[2023 Venture Capital Report](https://www.wilmerhale.com/-/media/files/shared_content/editorial/publications/documents/2023-wilmerhale-vc-report.pdf)\n", 99 | "\n", 100 | "This is a Venture Capital report of 2023 issued by WilmerHale." 101 | ], 102 | "metadata": { 103 | "id": "O2f7XFsu8BIK" 104 | } 105 | }, 106 | { 107 | "cell_type": "markdown", 108 | "source": [ 109 | "### 1. Load" 110 | ], 111 | "metadata": { 112 | "id": "qUpdc89s8lmd" 113 | } 114 | }, 115 | { 116 | "cell_type": "code", 117 | "source": [ 118 | "from dotenv import load_dotenv\n", 119 | "load_dotenv()\n", 120 | "\n", 121 | "import os" 122 | ], 123 | "metadata": { 124 | "id": "ywRTtMmR2UKV" 125 | }, 126 | "execution_count": 107, 127 | "outputs": [] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "source": [ 132 | "openai_api_key = os.environ[\"OPENAI_API_KEY\"]" 133 | ], 134 | "metadata": { 135 | "id": "GeW4OJ6Y2rR7" 136 | }, 137 | "execution_count": 108, 138 | "outputs": [] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "source": [ 143 | "import openai\n", 144 | "\n", 145 | "# You don't have to explicitly fetch the API key from environmental variables and assign it.\n", 146 | "# OpenAI class will load it from env var OPENAI_API_KEY automatically.\n", 147 | "\n", 148 | "openai_client = openai.OpenAI(api_key=openai_api_key)" 149 | ], 150 | "metadata": { 151 | "id": "LPz0oCZkEcwb" 152 | }, 153 | "execution_count": 65, 154 | "outputs": [] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "source": [ 159 | "### 2. Retrieve existing files\n", 160 | "\n", 161 | "The files uploaded via `OpenAI Files API` are persisted. They can be surely reused by referring to their ids.\n", 162 | "\n", 163 | "In this step, we will retrieve the files already uploaded and see if the PDF file `2023-WilmerHale-VC-Report.pdf` exists.\n", 164 | "\n", 165 | "If so, let's delete and upload again." 166 | ], 167 | "metadata": { 168 | "id": "lNi_L5_V9mZz" 169 | } 170 | }, 171 | { 172 | "cell_type": "code", 173 | "source": [ 174 | "# Retrieve the file list\n", 175 | "\n", 176 | "uploaded_files = openai_client.files.list()" 177 | ], 178 | "metadata": { 179 | "id": "2LTwlGShzYJd" 180 | }, 181 | "execution_count": 127, 182 | "outputs": [] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "source": [ 187 | "uploaded_files.data" 188 | ], 189 | "metadata": { 190 | "colab": { 191 | "base_uri": "https://localhost:8080/" 192 | }, 193 | "id": "s0YAls8qzkoH", 194 | "outputId": "a2652496-3e9c-4c03-9ecf-a0277a063109" 195 | }, 196 | "execution_count": 128, 197 | "outputs": [ 198 | { 199 | "output_type": "execute_result", 200 | "data": { 201 | "text/plain": [ 202 | "[FileObject(id='file-KC7h7nJFf2XBmXGJP2Arzzno', bytes=24117248, created_at=1700154133, filename='Standford_AI-Index-Report_2023.pdf', object='file', purpose='assistants', status='processed', status_details=None)]" 203 | ] 204 | }, 205 | "metadata": {}, 206 | "execution_count": 128 207 | } 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "source": [ 213 | "# Find the file by name\n", 214 | "\n", 215 | "filename_to_find = '2023-WilmerHale-VC-Report.pdf'\n", 216 | "the_file_id = None\n", 217 | "\n", 218 | "file_objects = list(filter(lambda x: x.filename == filename_to_find, uploaded_files.data))\n", 219 | "\n", 220 | "if len(file_objects) > 0:\n", 221 | " the_file_id = file_objects[0].id" 222 | ], 223 | "metadata": { 224 | "id": "aROVPPAl-a-m" 225 | }, 226 | "execution_count": 124, 227 | "outputs": [] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "source": [ 232 | "the_file_id" 233 | ], 234 | "metadata": { 235 | "colab": { 236 | "base_uri": "https://localhost:8080/", 237 | "height": 36 238 | }, 239 | "id": "m0NMZ59sIC-N", 240 | "outputId": "8287e04c-d9c3-4b5a-837f-64c5ae444d3b" 241 | }, 242 | "execution_count": 126, 243 | "outputs": [ 244 | { 245 | "output_type": "execute_result", 246 | "data": { 247 | "text/plain": [ 248 | "'file-1zy5eXDx5nDotjUp4tybtNGl'" 249 | ], 250 | "application/vnd.google.colaboratory.intrinsic+json": { 251 | "type": "string" 252 | } 253 | }, 254 | "metadata": {}, 255 | "execution_count": 126 256 | } 257 | ] 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "source": [ 262 | "Delete if it already exists.\n", 263 | "\n", 264 | "Notice that, this is for demonstration purpose." 265 | ], 266 | "metadata": { 267 | "id": "IJVmHdvP_wB1" 268 | } 269 | }, 270 | { 271 | "cell_type": "code", 272 | "source": [ 273 | "if the_file_id:\n", 274 | " delete_status = openai_client.files.delete(the_file_id)\n", 275 | " delete_status" 276 | ], 277 | "metadata": { 278 | "id": "DVouWaqgzr4Y" 279 | }, 280 | "execution_count": 125, 281 | "outputs": [] 282 | }, 283 | { 284 | "cell_type": "markdown", 285 | "source": [ 286 | "### 2. Upload the PDF file\n", 287 | "\n", 288 | "This PDF file will be the knowledge base of the RAG application." 289 | ], 290 | "metadata": { 291 | "id": "eBpTSwLQ_2we" 292 | } 293 | }, 294 | { 295 | "cell_type": "code", 296 | "source": [ 297 | "file = openai_client.files.create(\n", 298 | " file=open(\"2023-WilmerHale-VC-Report.pdf\", \"rb\"),\n", 299 | " purpose='assistants'\n", 300 | ")" 301 | ], 302 | "metadata": { 303 | "id": "MIbK3o4XFH1N" 304 | }, 305 | "execution_count": 129, 306 | "outputs": [] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "source": [ 311 | "file" 312 | ], 313 | "metadata": { 314 | "colab": { 315 | "base_uri": "https://localhost:8080/" 316 | }, 317 | "id": "4BEJW7lxFXVX", 318 | "outputId": "58cd48e1-3320-49d6-d4c9-64c076ef328e" 319 | }, 320 | "execution_count": 130, 321 | "outputs": [ 322 | { 323 | "output_type": "execute_result", 324 | "data": { 325 | "text/plain": [ 326 | "FileObject(id='file-p1e3aGURKtzkR83g91YUTBhn', bytes=1310948, created_at=1700171663, filename='2023-WilmerHale-VC-Report.pdf', object='file', purpose='assistants', status='processed', status_details=None)" 327 | ] 328 | }, 329 | "metadata": {}, 330 | "execution_count": 130 331 | } 332 | ] 333 | }, 334 | { 335 | "cell_type": "markdown", 336 | "source": [ 337 | "### 3. Retrieve the file by id\n", 338 | "\n", 339 | "This is to make sure the file is successfully uploaded." 340 | ], 341 | "metadata": { 342 | "id": "agL44d-1__wU" 343 | } 344 | }, 345 | { 346 | "cell_type": "code", 347 | "source": [ 348 | "retrieved_file = openai_client.files.retrieve(file.id)\n", 349 | "retrieved_file" 350 | ], 351 | "metadata": { 352 | "colab": { 353 | "base_uri": "https://localhost:8080/" 354 | }, 355 | "id": "3vg3-7t3cb2_", 356 | "outputId": "c305d4f6-3174-44d5-a97e-8cefe203d7b2" 357 | }, 358 | "execution_count": 131, 359 | "outputs": [ 360 | { 361 | "output_type": "execute_result", 362 | "data": { 363 | "text/plain": [ 364 | "FileObject(id='file-p1e3aGURKtzkR83g91YUTBhn', bytes=1310948, created_at=1700171663, filename='2023-WilmerHale-VC-Report.pdf', object='file', purpose='assistants', status='processed', status_details=None)" 365 | ] 366 | }, 367 | "metadata": {}, 368 | "execution_count": 131 369 | } 370 | ] 371 | }, 372 | { 373 | "cell_type": "markdown", 374 | "source": [ 375 | "### 4. Create an Assistant\n", 376 | "\n", 377 | "This Assistant will use the tool `Retrieval` and get associated with the PDF file uploaded by its id." 378 | ], 379 | "metadata": { 380 | "id": "ju7oOHNfAI2D" 381 | } 382 | }, 383 | { 384 | "cell_type": "code", 385 | "source": [ 386 | "assistant = openai_client.beta.assistants.create(\n", 387 | " instructions=\"Use the file provided as your knowledge base to best respond to customer queries.\",\n", 388 | " model=\"gpt-4-1106-preview\",\n", 389 | " tools=[\n", 390 | " { \"type\": \"retrieval\" }\n", 391 | " ],\n", 392 | " file_ids=[retrieved_file.id]\n", 393 | ")" 394 | ], 395 | "metadata": { 396 | "id": "iwD1noKvFds9" 397 | }, 398 | "execution_count": 132, 399 | "outputs": [] 400 | }, 401 | { 402 | "cell_type": "markdown", 403 | "source": [ 404 | "### 5. Retrieve the created Assistant\n", 405 | "\n", 406 | "It's retrieved by the assistant id.\n", 407 | "\n", 408 | "We must make sure in the response, we can see expected tool and file are associated with it." 409 | ], 410 | "metadata": { 411 | "id": "uAZ0Y-egATbn" 412 | } 413 | }, 414 | { 415 | "cell_type": "code", 416 | "source": [ 417 | "my_assistant = openai_client.beta.assistants.retrieve(assistant.id)\n", 418 | "my_assistant" 419 | ], 420 | "metadata": { 421 | "colab": { 422 | "base_uri": "https://localhost:8080/" 423 | }, 424 | "id": "YU7XgHKfcJmG", 425 | "outputId": "5a8bfafb-ed28-4ce9-b7ee-cd7cf718ca54" 426 | }, 427 | "execution_count": 133, 428 | "outputs": [ 429 | { 430 | "output_type": "execute_result", 431 | "data": { 432 | "text/plain": [ 433 | "Assistant(id='asst_YYN7UKBkyuFs5UghtNfzxO3z', created_at=1700171729, description=None, file_ids=['file-p1e3aGURKtzkR83g91YUTBhn'], instructions='Use the file provided as your knowledge base to best respond to customer queries.', metadata={}, model='gpt-4-1106-preview', name=None, object='assistant', tools=[ToolRetrieval(type='retrieval')])" 434 | ] 435 | }, 436 | "metadata": {}, 437 | "execution_count": 133 438 | } 439 | ] 440 | }, 441 | { 442 | "cell_type": "markdown", 443 | "source": [ 444 | "### 6. (Optional) Update the Assistant\n", 445 | "\n", 446 | "I noticed once that the created assistant didn't have the tool and file associated.\n", 447 | "\n", 448 | "When it happens to you, use the `update` function to associate them again." 449 | ], 450 | "metadata": { 451 | "id": "Gyba9nuNAgqu" 452 | } 453 | }, 454 | { 455 | "cell_type": "code", 456 | "source": [ 457 | "updated_assistant = openai_client.beta.assistants.update(\n", 458 | " assistant.id,\n", 459 | " tools=[{\"type\": \"retrieval\"}],\n", 460 | " file_ids=[retrieved_file.id],\n", 461 | ")\n", 462 | "\n", 463 | "updated_assistant" 464 | ], 465 | "metadata": { 466 | "colab": { 467 | "base_uri": "https://localhost:8080/" 468 | }, 469 | "id": "Am6RzSLfeEs3", 470 | "outputId": "a206f689-2af1-4a3d-e6ab-b02a62bbd271" 471 | }, 472 | "execution_count": 134, 473 | "outputs": [ 474 | { 475 | "output_type": "execute_result", 476 | "data": { 477 | "text/plain": [ 478 | "Assistant(id='asst_YYN7UKBkyuFs5UghtNfzxO3z', created_at=1700171729, description=None, file_ids=['file-p1e3aGURKtzkR83g91YUTBhn'], instructions='Use the file provided as your knowledge base to best respond to customer queries.', metadata={}, model='gpt-4-1106-preview', name=None, object='assistant', tools=[ToolRetrieval(type='retrieval')])" 479 | ] 480 | }, 481 | "metadata": {}, 482 | "execution_count": 134 483 | } 484 | ] 485 | }, 486 | { 487 | "cell_type": "markdown", 488 | "source": [ 489 | "### 7. Create a Thread" 490 | ], 491 | "metadata": { 492 | "id": "e1pi3ta8AvX4" 493 | } 494 | }, 495 | { 496 | "cell_type": "code", 497 | "source": [ 498 | "thread = openai_client.beta.threads.create()\n", 499 | "\n", 500 | "thread" 501 | ], 502 | "metadata": { 503 | "colab": { 504 | "base_uri": "https://localhost:8080/" 505 | }, 506 | "id": "5H1cR3XdF0R1", 507 | "outputId": "6adcb900-cad0-437b-ee24-15a44ac39504" 508 | }, 509 | "execution_count": 135, 510 | "outputs": [ 511 | { 512 | "output_type": "execute_result", 513 | "data": { 514 | "text/plain": [ 515 | "Thread(id='thread_PIN1poM9aRX4sYjnnohlvZNa', created_at=1700171825, metadata={}, object='thread')" 516 | ] 517 | }, 518 | "metadata": {}, 519 | "execution_count": 135 520 | } 521 | ] 522 | }, 523 | { 524 | "cell_type": "markdown", 525 | "source": [ 526 | "### 8. Create a Message\n", 527 | "\n", 528 | "We are going to use a message object to request the Assistant to extract the content architecture out of the PDF file." 529 | ], 530 | "metadata": { 531 | "id": "PiCMxyJNAyEU" 532 | } 533 | }, 534 | { 535 | "cell_type": "code", 536 | "source": [ 537 | "thread_message = openai_client.beta.threads.messages.create(\n", 538 | " thread_id=thread.id,\n", 539 | " role=\"user\",\n", 540 | " content=\"Please show me the content architecture of this report\",\n", 541 | ")\n", 542 | "thread_message" 543 | ], 544 | "metadata": { 545 | "colab": { 546 | "base_uri": "https://localhost:8080/" 547 | }, 548 | "id": "sD4YL8apMoUV", 549 | "outputId": "7c31e5ba-499d-4e3b-a2dd-e05d823987fd" 550 | }, 551 | "execution_count": 136, 552 | "outputs": [ 553 | { 554 | "output_type": "execute_result", 555 | "data": { 556 | "text/plain": [ 557 | "ThreadMessage(id='msg_r00GeAZ3q4zaQYxaYlGsjMmd', assistant_id=None, content=[MessageContentText(text=Text(annotations=[], value='Please show me the content architecture of this report'), type='text')], created_at=1700171844, file_ids=[], metadata={}, object='thread.message', role='user', run_id=None, thread_id='thread_PIN1poM9aRX4sYjnnohlvZNa')" 558 | ] 559 | }, 560 | "metadata": {}, 561 | "execution_count": 136 562 | } 563 | ] 564 | }, 565 | { 566 | "cell_type": "markdown", 567 | "source": [ 568 | "### 9. Create a Run\n", 569 | "\n", 570 | "A `Run` triggers the interaction to the LLM." 571 | ], 572 | "metadata": { 573 | "id": "x8jMsm-DBG8R" 574 | } 575 | }, 576 | { 577 | "cell_type": "code", 578 | "source": [ 579 | "run = openai_client.beta.threads.runs.create(\n", 580 | " thread_id=thread.id,\n", 581 | " assistant_id=updated_assistant.id\n", 582 | ")" 583 | ], 584 | "metadata": { 585 | "id": "Yp1Fr_FEL65n" 586 | }, 587 | "execution_count": 137, 588 | "outputs": [] 589 | }, 590 | { 591 | "cell_type": "markdown", 592 | "source": [ 593 | "### 10. Retrieve the Run\n", 594 | "\n", 595 | "The `Run` is done in async mode, so we need to query the status of the Run by id." 596 | ], 597 | "metadata": { 598 | "id": "o7HYAnqABRay" 599 | } 600 | }, 601 | { 602 | "cell_type": "code", 603 | "source": [ 604 | "retrieved_run = openai_client.beta.threads.runs.retrieve(\n", 605 | " thread_id=thread.id,\n", 606 | " run_id=run.id\n", 607 | ")" 608 | ], 609 | "metadata": { 610 | "id": "rCv4p11TNJUu" 611 | }, 612 | "execution_count": 138, 613 | "outputs": [] 614 | }, 615 | { 616 | "cell_type": "code", 617 | "source": [ 618 | "retrieved_run" 619 | ], 620 | "metadata": { 621 | "colab": { 622 | "base_uri": "https://localhost:8080/" 623 | }, 624 | "id": "nILYSfeINRnw", 625 | "outputId": "6ca7eb08-dcdd-4d09-c52f-c5556b800fd6" 626 | }, 627 | "execution_count": 139, 628 | "outputs": [ 629 | { 630 | "output_type": "execute_result", 631 | "data": { 632 | "text/plain": [ 633 | "Run(id='run_uGwTGAemL5rqFwWlHc02Wxwl', assistant_id='asst_YYN7UKBkyuFs5UghtNfzxO3z', cancelled_at=None, completed_at=1700171869, created_at=1700171854, expires_at=None, failed_at=None, file_ids=['file-p1e3aGURKtzkR83g91YUTBhn'], instructions='Use the file provided as your knowledge base to best respond to customer queries.', last_error=None, metadata={}, model='gpt-4-1106-preview', object='thread.run', required_action=None, started_at=1700171854, status='completed', thread_id='thread_PIN1poM9aRX4sYjnnohlvZNa', tools=[ToolAssistantToolsRetrieval(type='retrieval')])" 634 | ] 635 | }, 636 | "metadata": {}, 637 | "execution_count": 139 638 | } 639 | ] 640 | }, 641 | { 642 | "cell_type": "markdown", 643 | "source": [ 644 | "### 11. Retrieve the message list of the Thread\n", 645 | "\n", 646 | "Until the Run is completed, retrieve the message list and fetch the latest message of the list which is the response of the LLM." 647 | ], 648 | "metadata": { 649 | "id": "fCcSFIXoBc6i" 650 | } 651 | }, 652 | { 653 | "cell_type": "code", 654 | "source": [ 655 | "thread_messages = openai_client.beta.threads.messages.list(thread.id)\n", 656 | "thread_messages.data" 657 | ], 658 | "metadata": { 659 | "colab": { 660 | "base_uri": "https://localhost:8080/" 661 | }, 662 | "id": "dAbqv9S7Ne3n", 663 | "outputId": "37a14842-5c11-47db-e177-d5ef1ae63a09" 664 | }, 665 | "execution_count": 140, 666 | "outputs": [ 667 | { 668 | "output_type": "execute_result", 669 | "data": { 670 | "text/plain": [ 671 | "[ThreadMessage(id='msg_UYQX4LrKPLpVNjhjBQRUGO07', assistant_id='asst_YYN7UKBkyuFs5UghtNfzxO3z', content=[MessageContentText(text=Text(annotations=[TextAnnotationFileCitation(end_index=817, file_citation=TextAnnotationFileCitationFileCitation(file_id='file-p1e3aGURKtzkR83g91YUTBhn', quote='2023 Venture Capital Report – What’s Inside\\n\\n\\n2 US Market Review and Outlook\\n\\n\\n6 Regional Market Review and Outlook\\n\\n\\n10 Selected WilmerHale Venture Capital Financings\\n\\n\\n12 New Law Requires Federal Reporting of Private Company Ownership\\nMany Startups and Life Sciences Companies Will be Subject to Beneficial Ownership\\nReporting\\n\\n\\n13 Show Me the Money\\nWhat Employers Need to Know About New Salary Disclosure Laws\\n\\n\\n14 Navigating the Quiet Period Shoals\\nSafe Harbors Aid Compliance With Quiet Period Requirements\\n\\n\\n16 State Taxation of Qualified Small Business Stock\\nFederal Tax Exclusion Not Always Replicated at State Level\\n\\n\\n17 Trends in VC-Backed Company M&A Deal Terms\\n\\n\\n18 Trends in Convertible Note and SAFE Terms\\n\\n\\n19 Trends in Venture Capital Financing Terms'), start_index=807, text='【7†source】', type='file_citation')], value='The content architecture of the 2023 Venture Capital Report includes the following sections:\\n\\n1. US Market Review and Outlook\\n2. Regional Market Review and Outlook\\n3. Selected WilmerHale Venture Capital Financings\\n4. New Law Requires Federal Reporting of Private Company Ownership (Many Startups and Life Sciences Companies Will be Subject to Beneficial Ownership Reporting)\\n5. Show Me the Money (What Employers Need to Know About New Salary Disclosure Laws)\\n6. Navigating the Quiet Period Shoals (Safe Harbors Aid Compliance With Quiet Period Requirements)\\n7. State Taxation of Qualified Small Business Stock (Federal Tax Exclusion Not Always Replicated at State Level)\\n8. Trends in VC-Backed Company M&A Deal Terms\\n9. Trends in Convertible Note and SAFE Terms\\n10. Trends in Venture Capital Financing Terms【7†source】.'), type='text')], created_at=1700171861, file_ids=[], metadata={}, object='thread.message', role='assistant', run_id='run_uGwTGAemL5rqFwWlHc02Wxwl', thread_id='thread_PIN1poM9aRX4sYjnnohlvZNa'),\n", 672 | " ThreadMessage(id='msg_r00GeAZ3q4zaQYxaYlGsjMmd', assistant_id=None, content=[MessageContentText(text=Text(annotations=[], value='Please show me the content architecture of this report'), type='text')], created_at=1700171844, file_ids=[], metadata={}, object='thread.message', role='user', run_id=None, thread_id='thread_PIN1poM9aRX4sYjnnohlvZNa')]" 673 | ] 674 | }, 675 | "metadata": {}, 676 | "execution_count": 140 677 | } 678 | ] 679 | }, 680 | { 681 | "cell_type": "code", 682 | "source": [ 683 | "print(thread_messages.data[0].content[0].text.value)" 684 | ], 685 | "metadata": { 686 | "colab": { 687 | "base_uri": "https://localhost:8080/" 688 | }, 689 | "id": "cxVMc18Tem5u", 690 | "outputId": "51201c38-9374-4157-c38d-7ae718b87a77" 691 | }, 692 | "execution_count": 141, 693 | "outputs": [ 694 | { 695 | "output_type": "stream", 696 | "name": "stdout", 697 | "text": [ 698 | "The content architecture of the 2023 Venture Capital Report includes the following sections:\n", 699 | "\n", 700 | "1. US Market Review and Outlook\n", 701 | "2. Regional Market Review and Outlook\n", 702 | "3. Selected WilmerHale Venture Capital Financings\n", 703 | "4. New Law Requires Federal Reporting of Private Company Ownership (Many Startups and Life Sciences Companies Will be Subject to Beneficial Ownership Reporting)\n", 704 | "5. Show Me the Money (What Employers Need to Know About New Salary Disclosure Laws)\n", 705 | "6. Navigating the Quiet Period Shoals (Safe Harbors Aid Compliance With Quiet Period Requirements)\n", 706 | "7. State Taxation of Qualified Small Business Stock (Federal Tax Exclusion Not Always Replicated at State Level)\n", 707 | "8. Trends in VC-Backed Company M&A Deal Terms\n", 708 | "9. Trends in Convertible Note and SAFE Terms\n", 709 | "10. Trends in Venture Capital Financing Terms【7†source】.\n" 710 | ] 711 | } 712 | ] 713 | } 714 | ] 715 | } -------------------------------------------------------------------------------- /openai_meta_prompt.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "provenance": [], 7 | "authorship_tag": "ABX9TyP45+rZjFXKdMVZSc80F91m", 8 | "include_colab_link": true 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | }, 14 | "language_info": { 15 | "name": "python" 16 | } 17 | }, 18 | "cells": [ 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "id": "view-in-github", 23 | "colab_type": "text" 24 | }, 25 | "source": [ 26 | "\"Open" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 30, 32 | "metadata": { 33 | "id": "l4_Mh6q4lmNo" 34 | }, 35 | "outputs": [], 36 | "source": [ 37 | "!pip install openai -qU" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "source": [ 43 | "from google.colab import userdata\n", 44 | "OPENAI_API_KEY = userdata.get('OPENAI_API_KEY')" 45 | ], 46 | "metadata": { 47 | "id": "89_EbmbBlt_W" 48 | }, 49 | "execution_count": 31, 50 | "outputs": [] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "source": [ 55 | "from openai import OpenAI\n", 56 | "\n", 57 | "client = OpenAI(api_key=OPENAI_API_KEY)\n", 58 | "\n", 59 | "META_PROMPT = \"\"\"\n", 60 | "Given a task description or existing prompt, produce a detailed system prompt to guide a language model in completing the task effectively.\n", 61 | "\n", 62 | "# Guidelines\n", 63 | "\n", 64 | "- Understand the Task: Grasp the main objective, goals, requirements, constraints, and expected output.\n", 65 | "- Minimal Changes: If an existing prompt is provided, improve it only if it's simple. For complex prompts, enhance clarity and add missing elements without altering the original structure.\n", 66 | "- Reasoning Before Conclusions**: Encourage reasoning steps before any conclusions are reached. ATTENTION! If the user provides examples where the reasoning happens afterward, REVERSE the order! NEVER START EXAMPLES WITH CONCLUSIONS!\n", 67 | " - Reasoning Order: Call out reasoning portions of the prompt and conclusion parts (specific fields by name). For each, determine the ORDER in which this is done, and whether it needs to be reversed.\n", 68 | " - Conclusion, classifications, or results should ALWAYS appear last.\n", 69 | "- Examples: Include high-quality examples if helpful, using placeholders [in brackets] for complex elements.\n", 70 | " - What kinds of examples may need to be included, how many, and whether they are complex enough to benefit from placeholders.\n", 71 | "- Clarity and Conciseness: Use clear, specific language. Avoid unnecessary instructions or bland statements.\n", 72 | "- Formatting: Use markdown features for readability. DO NOT USE ``` CODE BLOCKS UNLESS SPECIFICALLY REQUESTED.\n", 73 | "- Preserve User Content: If the input task or prompt includes extensive guidelines or examples, preserve them entirely, or as closely as possible. If they are vague, consider breaking down into sub-steps. Keep any details, guidelines, examples, variables, or placeholders provided by the user.\n", 74 | "- Constants: DO include constants in the prompt, as they are not susceptible to prompt injection. Such as guides, rubrics, and examples.\n", 75 | "- Output Format: Explicitly the most appropriate output format, in detail. This should include length and syntax (e.g. short sentence, paragraph, JSON, etc.)\n", 76 | " - For tasks outputting well-defined or structured data (classification, JSON, etc.) bias toward outputting a JSON.\n", 77 | " - JSON should never be wrapped in code blocks (```) unless explicitly requested.\n", 78 | "\n", 79 | "The final prompt you output should adhere to the following structure below. Do not include any additional commentary, only output the completed system prompt. SPECIFICALLY, do not include any additional messages at the start or end of the prompt. (e.g. no \"---\")\n", 80 | "\n", 81 | "[Concise instruction describing the task - this should be the first line in the prompt, no section header]\n", 82 | "\n", 83 | "[Additional details as needed.]\n", 84 | "\n", 85 | "[Optional sections with headings or bullet points for detailed steps.]\n", 86 | "\n", 87 | "# Steps [optional]\n", 88 | "\n", 89 | "[optional: a detailed breakdown of the steps necessary to accomplish the task]\n", 90 | "\n", 91 | "# Output Format\n", 92 | "\n", 93 | "[Specifically call out how the output should be formatted, be it response length, structure e.g. JSON, markdown, etc]\n", 94 | "\n", 95 | "# Examples [optional]\n", 96 | "\n", 97 | "[Optional: 1-3 well-defined examples with placeholders if necessary. Clearly mark where examples start and end, and what the input and output are. User placeholders as necessary.]\n", 98 | "[If the examples are shorter than what a realistic example is expected to be, make a reference with () explaining how real examples should be longer / shorter / different. AND USE PLACEHOLDERS! ]\n", 99 | "\n", 100 | "# Notes [optional]\n", 101 | "\n", 102 | "[optional: edge cases, details, and an area to call or repeat out specific important considerations]\n", 103 | "\"\"\".strip()\n", 104 | "\n", 105 | "def generate_prompt(task_or_prompt: str):\n", 106 | " completion = client.chat.completions.create(\n", 107 | " model=\"gpt-4o\",\n", 108 | " messages=[\n", 109 | " {\n", 110 | " \"role\": \"system\",\n", 111 | " \"content\": META_PROMPT,\n", 112 | " },\n", 113 | " {\n", 114 | " \"role\": \"user\",\n", 115 | " \"content\": \"Task, Goal, or Current Prompt:\\n\" + task_or_prompt,\n", 116 | " },\n", 117 | " ],\n", 118 | " )\n", 119 | "\n", 120 | " return completion.choices[0].message.content" 121 | ], 122 | "metadata": { 123 | "id": "PytAbZNslrQx" 124 | }, 125 | "execution_count": 32, 126 | "outputs": [] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "source": [ 131 | "prompt = generate_prompt(\"Optimize the prompt that users enter for image generation with Flux 1.1 model\")" 132 | ], 133 | "metadata": { 134 | "id": "GyNBRYEImMbD" 135 | }, 136 | "execution_count": 33, 137 | "outputs": [] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "source": [ 142 | "from IPython.display import Markdown, display\n", 143 | "\n", 144 | "def print_md(string):\n", 145 | " display(Markdown(string))" 146 | ], 147 | "metadata": { 148 | "id": "TlNRtAJe3Nww" 149 | }, 150 | "execution_count": 35, 151 | "outputs": [] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "source": [ 156 | "print_md(prompt)" 157 | ], 158 | "metadata": { 159 | "colab": { 160 | "base_uri": "https://localhost:8080/", 161 | "height": 558 162 | }, 163 | "id": "ua48AmG4mdBK", 164 | "outputId": "0a5c77c5-f3ae-489b-d368-a7f2fb7bb602" 165 | }, 166 | "execution_count": 36, 167 | "outputs": [ 168 | { 169 | "output_type": "display_data", 170 | "data": { 171 | "text/plain": [ 172 | "" 173 | ], 174 | "text/markdown": "Optimize user input prompts for image generation with the Flux 1.1 model by enhancing clarity, specificity, and focus on desired visual elements.\n\n- Assess the user's original prompt, identifying any vagueness or lack of detail.\n- Clarify the main subject or theme of the image the user wants to generate.\n- Specify visual details such as colors, styles, lighting, and composition.\n- Include specific instructions for any particular elements, objects, or scenes.\n- Ensure the prompt aligns with the capabilities and strengths of the Flux 1.1 model.\n- Use precise and evocative language to enhance visual imagination.\n\n# Steps\n\n1. **Analyze the Original Prompt**: Identify areas that need clarification or expansion.\n2. **Clarify Desired Subject**: Specify the main subject and its context.\n3. **Detail Visual Elements**: Include details on textures, colors, lighting, and perspective.\n4. **Specify Composition**: Define the layout, balance, and any focal points.\n5. **Refine Language**: Use vivid and specific language to convey the visual idea clearly.\n\n# Output Format\n\n- Present the optimized prompt as a single cohesive text.\n- Ensure clarity and focus in the language that guides the image generation effectively.\n\n# Examples\n\n**Original Prompt**: \"A sunset over a forest\"\n\n**Optimized Prompt**: \"A vibrant sunset painting the sky in hues of orange and pink, casting golden light over a dense forest of tall pine trees. The sun is partially hidden behind the horizon, creating intricate shadows and highlights on the tree foliage, with a serene lake reflecting the colorful sky in the foreground.\"\n\n(Note: Real examples should be longer and more detailed to accommodate specific needs of the Flux 1.1 model.)" 175 | }, 176 | "metadata": {} 177 | } 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "source": [ 183 | "def ask(query, system_prompt):\n", 184 | " completion = client.chat.completions.create(\n", 185 | " model=\"gpt-4o\",\n", 186 | " messages=[\n", 187 | " {\n", 188 | " \"role\": \"system\",\n", 189 | " \"content\": system_prompt\n", 190 | " },\n", 191 | " {\n", 192 | " \"role\": \"user\",\n", 193 | " \"content\": query,\n", 194 | " },\n", 195 | " ],\n", 196 | " )\n", 197 | "\n", 198 | " return completion.choices[0].message.content" 199 | ], 200 | "metadata": { 201 | "id": "VsMCebcQnWUj" 202 | }, 203 | "execution_count": 38, 204 | "outputs": [] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "source": [ 209 | "ask(\"a lady with fashion dress, sit by a car. she has a cigaret in her fingers. She wears sunglasses. She is hot\", prompt)" 210 | ], 211 | "metadata": { 212 | "colab": { 213 | "base_uri": "https://localhost:8080/", 214 | "height": 91 215 | }, 216 | "id": "8NMjSwLrnjXj", 217 | "outputId": "e4034668-784f-4c81-e038-65ed87b2c0ff" 218 | }, 219 | "execution_count": 39, 220 | "outputs": [ 221 | { 222 | "output_type": "execute_result", 223 | "data": { 224 | "text/plain": [ 225 | "\"A stylish woman dressed in a chic, designer fashion outfit sits confidently by a sleek, modern car. Her ensemble features a form-fitting, elegant dress in deep royal blue with intricate gold patterns that catch the light. She leans casually against the car, exuding an aura of cool sophistication. In her right hand, she delicately holds a cigarette, the smoke swirling gently upwards. Her oversized, dark sunglasses rest stylishly on her nose, partially obscuring her face but hinting at sharp, striking features. The scene is set in a sun-drenched urban environment, where the sunlight glistens off the car's polished surface, adding to the overall glamorous atmosphere.\"" 226 | ], 227 | "application/vnd.google.colaboratory.intrinsic+json": { 228 | "type": "string" 229 | } 230 | }, 231 | "metadata": {}, 232 | "execution_count": 39 233 | } 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "source": [ 239 | "financial_analysis_prompt = generate_prompt(\"Analyze the company's financial report and provide a comprehensive financial analysis as a professional financial analyst\")" 240 | ], 241 | "metadata": { 242 | "id": "bCroDNFypE30" 243 | }, 244 | "execution_count": 40, 245 | "outputs": [] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "source": [ 250 | "print_md(financial_analysis_prompt)" 251 | ], 252 | "metadata": { 253 | "colab": { 254 | "base_uri": "https://localhost:8080/", 255 | "height": 888 256 | }, 257 | "id": "hLiXHFbIpXY-", 258 | "outputId": "c7ab6d3d-8aec-447b-d47f-57027a7322b1" 259 | }, 260 | "execution_count": 41, 261 | "outputs": [ 262 | { 263 | "output_type": "display_data", 264 | "data": { 265 | "text/plain": [ 266 | "" 267 | ], 268 | "text/markdown": "Analyze a company's financial report and provide a comprehensive financial analysis from the perspective of a professional financial analyst.\n\n- Review key financial statements including the income statement, balance sheet, and cash flow statement.\n- Identify and interpret changes in revenue, profit margins, and any significant financial trends.\n- Highlight any areas of financial concern or interest, such as liquidity issues or strong performance metrics.\n- Compare the firm's current performance to industry benchmarks or historical performance if applicable.\n\n# Steps\n\n1. **Gather Data**: Collect the required financial statements: income statement, balance sheet, and cash flow statement.\n2. **Perform Ratio Analysis**: Calculate financial ratios such as gross profit margin, net profit margin, current ratio, Quick ratio, Return on Assets (ROA), and Return on Equity (ROE), and interpret these figures.\n3. **Trend Analysis**: Examine trends over multiple periods in key metrics such as revenue, net income, and any other central financial metrics.\n4. **Benchmark Comparison**: Compare the company's financial ratios and trends to industry averages or past performance when possible.\n5. **Identify Areas of Concern**: Highlight any signs of financial distress or exemplary performance such as high debt levels, liquidity problems, or exceptionally strong growth.\n6. **Conclusion**: Synthesize findings to provide a coherent narrative on the company’s financial status and potential future outlook.\n\n# Output Format\n\nProvide a structured analysis divided into the following sections:\n- **Executive Summary**: A concise overview of the key findings and conclusions.\n- **Detailed Financial Analysis**: Separate sections for ratio analysis, trend analysis, and benchmark comparison, with detailed explanations of calculations.\n- **Areas of Concern and Recommendations**: List any identified financial issues with proposed recommendations for improvement or further investigation.\n\n# Examples\n\n**Example (shortened for brevity):**\n\n- **Executive Summary**: The company shows a positive growth trend, with revenue increasing by 10% year-over-year. The current ratio indicates strong liquidity, though debt levels are slightly above industry average.\n- **Detailed Financial Analysis**:\n - *Ratio Analysis*: \n - Gross Profit Margin: 40%\n - Net Profit Margin: 15%\n - Liquidity Ratios: Current Ratio is 1.5, Quick Ratio is 1.2\n - *Trend Analysis*: Revenue has grown consistently over the past three years, with a compound annual growth rate (CAGR) of 12%.\n - *Benchmark Comparison*: Net margins and ROE are higher than industry averages, signaling efficient management.\n- **Areas of Concern and Recommendations**: Increase in accounts receivable days may indicate potential collection issues; focus on improving AR turnover.\n\n(For a complete analysis, each section should be expanded with specific figures and be much more detailed.)\n\n# Notes\n\n- Pay attention to any anomalies or one-time events affecting financial results.\n- Consider global economic factors or industry-specific challenges that may influence the financial outlook." 269 | }, 270 | "metadata": {} 271 | } 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "source": [ 277 | "report = '''\n", 278 | "Key Financial Results for the Second Quarter of 2024\n", 279 | "(in RMB million, except for percentage)\n", 280 | "2024 Q2 2024 Q1 2023 Q2 % Changeiii\n", 281 | "QoQ YoY\n", 282 | "Vehicle Sales 15,679.6 8,381.3 7,185.2 87.1% 118.2%\n", 283 | "Vehicle Margin 12.2% 9.2% 6.2% 300bp 600bp\n", 284 | "Total Revenues 17,446.0 9,908.6 8,771.7 76.1% 98.9%\n", 285 | "Gross Profit 1,688.7 487.7 87.0 246.3% 1,841.0%\n", 286 | "Gross Margin 9.7% 4.9% 1.0% 480bp 870bp\n", 287 | "Loss from Operations (5,209.3) (5,394.1) (6,074.1) -3.4% -14.2%\n", 288 | "Adjusted Loss from Operations (non-GAAP) (4,698.5) (5,112.7) (5,464.1) -8.1% -14.0%\n", 289 | "Net Loss (5,046.0) (5,184.6) (6,055.8) -2.7% -16.7%\n", 290 | "Adjusted Net Loss (non-GAAP) (4,535.2) (4,903.2) (5,445.7) -7.5% -16.7%\n", 291 | "'''" 292 | ], 293 | "metadata": { 294 | "id": "qcubUkBMpjmj" 295 | }, 296 | "execution_count": null, 297 | "outputs": [] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "source": [ 302 | "analysis = ask(report, financial_analysis_prompt)" 303 | ], 304 | "metadata": { 305 | "id": "GkhUj51fpqIt" 306 | }, 307 | "execution_count": 42, 308 | "outputs": [] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "source": [ 313 | "print_md(analysis)" 314 | ], 315 | "metadata": { 316 | "colab": { 317 | "base_uri": "https://localhost:8080/", 318 | "height": 724 319 | }, 320 | "id": "2wUmtm97p3kW", 321 | "outputId": "dc1734e6-0eff-4454-f9fd-b0a4d0496947" 322 | }, 323 | "execution_count": 43, 324 | "outputs": [ 325 | { 326 | "output_type": "display_data", 327 | "data": { 328 | "text/plain": [ 329 | "" 330 | ], 331 | "text/markdown": "**Executive Summary**: \nThe company's financial performance for Q2 2024 indicates strong growth in revenue and improvements in margins, despite continuing net losses. Vehicle sales increased significantly, outperforming both the previous quarter and the same quarter last year. Margins have improved, indicating enhanced cost management or pricing strategy. Although the net loss remains, it has reduced both sequentially and year-over-year, highlighting improvements in operational efficiency but indicating ongoing challenges in reaching profitability.\n\n**Detailed Financial Analysis**:\n\n- **Ratio Analysis**:\n - **Gross Profit Margin**: Increased substantially to 9.7% from 4.9% in Q1 2024 and 1.0% in Q2 2023, a positive indicator of cost control and operational efficiency.\n - **Net Profit Margin**: The net loss margin needs more context but represents potential for future positive turnovers if current trends continue.\n - **Vehicle Margin**: Improved to 12.2% from 9.2% in Q1 2024 and 6.2% in Q2 2023, highlighting better cost management in vehicle production or improved pricing power.\n \n- **Trend Analysis**:\n - **Revenue Growth**: Total revenues surged by 76.1% quarter-over-quarter (QoQ) and 98.9% year-over-year (YoY). This is driven mainly by vehicle sales, which increased by 87.1% QoQ and 118.2% YoY, indicating robust demand and potentially successful marketing or expansion strategies.\n - **Profitability**: While gross profit has dramatically improved, losses from operations and net loss have decreased both sequentially and from the previous year. This shows progress towards breakeven but also highlights the necessity for further improvements.\n\n- **Benchmark Comparison**:\n - Without specific industry benchmarks in this extract, the year-over-year and quarter-over-quarter improvements suggest better alignment with high-growth industry counterparts, especially given the significant advances in gross and vehicle margins.\n - The continued reduction in adjusted net losses compared to previous periods aligns well with a trend towards better operational efficiency compared to industry metrics often dominated by large losses in early growth stages.\n\n**Areas of Concern and Recommendations**:\n\n- **Profitability Concerns**: Despite significant revenue growth, profitability remains a concern with continued net losses. It is crucial to explore avenues to further improve operational efficiency and cost management.\n - **Recommendation**: Focus on continuous improvement in cost efficiencies, potentially through leveraging economies of scale or further enhancing production processes.\n \n- **Potential for Cash Burn**: The ongoing net losses could imply significant cash outflows. Ensuring adequate liquidity and reviewing financing strategies will be essential.\n - **Recommendation**: Re-evaluate cash flow strategy and explore cost optimization efforts to minimize continuing cash burn.\n\n- **Market and Demand Fluctuations**: Sustaining such high revenue growth rates may become challenging if market conditions change.\n - **Recommendation**: Diversify product offerings and revenue streams to mitigate reliance on vehicle sales.\n\nThese insights suggest a positive trajectory but emphasize the importance of strategic financial management to continue this momentum towards operational sustainability and eventual profitability." 332 | }, 333 | "metadata": {} 334 | } 335 | ] 336 | }, 337 | { 338 | "cell_type": "code", 339 | "source": [ 340 | "blogger = generate_prompt(\"Understand the whole content, and rewrite it. Make sure core content, idea or opinion is not changed, and the new content is maintained in a good structure and suitable for blogging.\")" 341 | ], 342 | "metadata": { 343 | "id": "qYIqLRkSqlj9" 344 | }, 345 | "execution_count": 44, 346 | "outputs": [] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "source": [ 351 | "print_md(blogger)" 352 | ], 353 | "metadata": { 354 | "colab": { 355 | "base_uri": "https://localhost:8080/", 356 | "height": 463 357 | }, 358 | "id": "txuxt1i25rEm", 359 | "outputId": "df066cfc-115d-46ac-b3b9-fdd857b3a4b4" 360 | }, 361 | "execution_count": 45, 362 | "outputs": [ 363 | { 364 | "output_type": "display_data", 365 | "data": { 366 | "text/plain": [ 367 | "" 368 | ], 369 | "text/markdown": "Revise and rewrite the provided content so that it remains faithful to the core ideas or opinions while enhancing its structure for blogging. Ensure clarity, coherence, and engagement in your revised version, making it suitable for a blog audience.\n\n# Steps\n\n1. **Read and Comprehend**: Fully understand the main ideas, opinions, and key information in the original content.\n2. **Identify Key Elements**: Pinpoint the core content that must remain unchanged.\n3. **Reorganize Structure**: Restructure the content to fit a typical blog format, which may include a catchy introduction, clear headings, and well-organized body paragraphs.\n4. **Rephrase and Rewrite**: Rewrite sentences and paragraphs to enhance readability and flow, ensuring the original meaning and opinion are retained.\n5. **Polish for Engagement**: Add elements to make the content more engaging, such as anecdotes, examples, or a conversational tone appropriate for a blog.\n6. **Proofread for Quality**: Check for grammar, spelling, and punctuation errors to ensure the final content is polished.\n\n# Output Format\n\nThe output should be structured as a blog article, with an engaging introduction, coherent body with subheadings if necessary, and a concluding paragraph. Aim for a natural flow and readability throughout the text.\n\n# Notes\n\n- Maintain the original tone and opinion without altering the core message.\n- Ensure the content appeals to a potential blog audience with appropriate language and style.\n- Consider the length of the original content; adapt it to fit standard blog post lengths if necessary." 370 | }, 371 | "metadata": {} 372 | } 373 | ] 374 | }, 375 | { 376 | "cell_type": "code", 377 | "source": [ 378 | "content = '''\n", 379 | "Cristiano Ronaldo appears to have joked that former Juventus teammate Wojciech Szczesny had to go into retirement in order to join \"a big club\" like Barcelona.\n", 380 | "\n", 381 | "Szczesny chose to retire this summer when it became clear Juventus were planning for a future without him, but ended his spell on the sidelines after just a few months to join Barcelona as an emergency replacement for Marc-Andre ter Stegen.\n", 382 | "\n", 383 | "Ronaldo was in Szczesny's homeland of Poland on Saturday on Portugal duty and the former Juventus teammates met up behind the scenes for a catch-up and photo.\n", 384 | "\n", 385 | "In footage captured by the Polish Football Federation, Ronaldo appeared to take a swipe at Juventus when congratulating Szczesny on his move to Barcelona.\n", 386 | "\n", 387 | "\"You needed to retire to go to a big club,\" Ronaldo told Szczesny.\n", 388 | "\n", 389 | "Ronaldo described Juve as an \"amazing club\" when he departed for Manchester United in 2021, but memories of his spell in Turin are split. He scored 101 goals in 134 games and won two Serie A titles in three years, but many have questioned whether Ronaldo improved the team as a whole.\n", 390 | "\n", 391 | "Earlier this year, Ronaldo found himself in legal action against Juventus over unpaid wages stemming from deferrals during the COVID-19 pandemic, with a court ultimately ruling he was owed a total of €10m.\n", 392 | "'''" 393 | ], 394 | "metadata": { 395 | "id": "_fuo2iVwrKDp" 396 | }, 397 | "execution_count": 46, 398 | "outputs": [] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "source": [ 403 | "blog = ask(content, blogger)" 404 | ], 405 | "metadata": { 406 | "id": "McLwfrG9sDl7" 407 | }, 408 | "execution_count": 47, 409 | "outputs": [] 410 | }, 411 | { 412 | "cell_type": "code", 413 | "source": [ 414 | "print_md(blog)" 415 | ], 416 | "metadata": { 417 | "colab": { 418 | "base_uri": "https://localhost:8080/", 419 | "height": 542 420 | }, 421 | "id": "hJvbWnOWsK-n", 422 | "outputId": "e65184e6-fe22-44aa-b895-8ab883a7098b" 423 | }, 424 | "execution_count": 48, 425 | "outputs": [ 426 | { 427 | "output_type": "display_data", 428 | "data": { 429 | "text/plain": [ 430 | "" 431 | ], 432 | "text/markdown": "### Ronaldo's Playful Jibe: From Juventus to Barcelona\n\nCristiano Ronaldo has made headlines once again, not for his moves on the pitch, but for a cheeky remark directed at his former Juventus teammate, Wojciech Szczesny. The two football stars reunited in Poland, where Portugal was playing, bringing a touch of humor to a conversation about Szczesny's latest career move.\n\n#### Szczesny's Journey: From Retirement to Barcelona\n\nWojciech Szczesny had announced his retirement over the summer, seemingly closing the chapter on his time at Juventus after the team decided to plan for the future without him. However, his retirement was short-lived. When Marc-Andre ter Stegen faced an unexpected absence, Barcelona called upon Szczesny as a last-minute, emergency solution. \n\nThis sudden shift from the sidelines back to the field with such a prestigious club was bound to spark some humorous exchanges—especially from a player like Ronaldo, known for his charismatic and sometimes cheeky personality.\n\n#### Ronaldo's Jest and Juventus Reflections\n\nDuring their catch-up, captured by the Polish Football Federation, Ronaldo congratulated Szczesny on his move to Barcelona but couldn't resist making a light-hearted comment: \"You needed to retire to go to a big club.\" This playful jab seemed to imply that Barcelona was a step up from their mutual former club, Juventus.\n\nRonaldo’s tenure at Juventus was a mixture of success and scrutiny. He hailed Juve as an \"amazing club\" when leaving for Manchester United in 2021. His individual record was impressive, with 101 goals in 134 appearances and two Serie A titles to his name. Yet, debates linger over whether he truly uplifted the team's overall performance during his stay. \n\n#### A Dispute Over Unpaid Wages\n\nAdding another layer to Ronaldo's story with Juventus is the legal scuffle they faced earlier this year. Ronaldo took the club to court over unpaid wages that were deferred during the COVID-19 pandemic. The court sided with Ronaldo, ordering Juventus to pay him €10 million.\n\n### Conclusion\n\nIn the world of football, where camaraderie and competition intertwine, Ronaldo's jest may simply reflect the lighthearted banter shared between two former teammates. As Szczesny embarks on his new journey with Barcelona, the football globe watches eagerly, waiting to see how these storylines continue to unfold. After all, in both football and friendship, it's these moments of humor and humanity that often capture the essence of the game." 433 | }, 434 | "metadata": {} 435 | } 436 | ] 437 | } 438 | ] 439 | } -------------------------------------------------------------------------------- /pandasai.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "provenance": [], 7 | "authorship_tag": "ABX9TyNK9/roNUSytGsRAJt1eXW7", 8 | "include_colab_link": true 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | }, 14 | "language_info": { 15 | "name": "python" 16 | } 17 | }, 18 | "cells": [ 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "id": "view-in-github", 23 | "colab_type": "text" 24 | }, 25 | "source": [ 26 | "\"Open" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "source": [ 32 | "# PandasAI\n", 33 | "\n", 34 | "PandasAI is a Python library that makes it easy to ask questions to your data (CSV, XLSX, PostgreSQL, MySQL, BigQuery, Databrick, Snowflake, etc.) in natural language.\n", 35 | "\n", 36 | "It helps you to explore, clean, and analyze your data using generative AI.\n", 37 | "\n", 38 | "GitHub: [https://github.com/Sinaptik-AI/pandas-ai](https://github.com/Sinaptik-AI/pandas-ai)\n", 39 | "\n", 40 | "Request Your Panda API Key: [https://www.pandabi.ai](https://www.pandabi.ai)\n" 41 | ], 42 | "metadata": { 43 | "id": "ZGfZ9MyAsWJ3" 44 | } 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 53, 49 | "metadata": { 50 | "colab": { 51 | "base_uri": "https://localhost:8080/" 52 | }, 53 | "id": "JG-2haLbZuDt", 54 | "outputId": "6cafa7b1-3725-419b-fc59-2bc67c5c592f" 55 | }, 56 | "outputs": [ 57 | { 58 | "output_type": "stream", 59 | "name": "stdout", 60 | "text": [ 61 | "Requirement already satisfied: pandasai in /usr/local/lib/python3.10/dist-packages (2.0.30)\n", 62 | "Requirement already satisfied: astor<0.9.0,>=0.8.1 in /usr/local/lib/python3.10/dist-packages (from pandasai) (0.8.1)\n", 63 | "Requirement already satisfied: duckdb<1 in /usr/local/lib/python3.10/dist-packages (from pandasai) (0.9.2)\n", 64 | "Requirement already satisfied: faker<20.0.0,>=19.12.0 in /usr/local/lib/python3.10/dist-packages (from pandasai) (19.13.0)\n", 65 | "Requirement already satisfied: jinja2<4.0.0,>=3.1.3 in /usr/local/lib/python3.10/dist-packages (from pandasai) (3.1.3)\n", 66 | "Requirement already satisfied: matplotlib<4.0.0,>=3.7.1 in /usr/local/lib/python3.10/dist-packages (from pandasai) (3.7.1)\n", 67 | "Requirement already satisfied: openai<2 in /usr/local/lib/python3.10/dist-packages (from pandasai) (1.16.2)\n", 68 | "Requirement already satisfied: pandas==1.5.3 in /usr/local/lib/python3.10/dist-packages (from pandasai) (1.5.3)\n", 69 | "Requirement already satisfied: pillow<11.0.0,>=10.1.0 in /usr/local/lib/python3.10/dist-packages (from pandasai) (10.3.0)\n", 70 | "Requirement already satisfied: pydantic<3,>=1 in /usr/local/lib/python3.10/dist-packages (from pandasai) (2.6.4)\n", 71 | "Requirement already satisfied: python-dotenv<2.0.0,>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from pandasai) (1.0.1)\n", 72 | "Requirement already satisfied: requests<3.0.0,>=2.31.0 in /usr/local/lib/python3.10/dist-packages (from pandasai) (2.31.0)\n", 73 | "Requirement already satisfied: scipy<2.0.0,>=1.9.0 in /usr/local/lib/python3.10/dist-packages (from pandasai) (1.11.4)\n", 74 | "Requirement already satisfied: sqlalchemy<3,>=1.4 in /usr/local/lib/python3.10/dist-packages (from pandasai) (2.0.29)\n", 75 | "Requirement already satisfied: python-dateutil>=2.8.1 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3->pandasai) (2.8.2)\n", 76 | "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3->pandasai) (2023.4)\n", 77 | "Requirement already satisfied: numpy>=1.21.0 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3->pandasai) (1.25.2)\n", 78 | "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2<4.0.0,>=3.1.3->pandasai) (2.1.5)\n", 79 | "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib<4.0.0,>=3.7.1->pandasai) (1.2.1)\n", 80 | "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib<4.0.0,>=3.7.1->pandasai) (0.12.1)\n", 81 | "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib<4.0.0,>=3.7.1->pandasai) (4.50.0)\n", 82 | "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib<4.0.0,>=3.7.1->pandasai) (1.4.5)\n", 83 | "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib<4.0.0,>=3.7.1->pandasai) (23.2)\n", 84 | "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib<4.0.0,>=3.7.1->pandasai) (3.1.2)\n", 85 | "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from openai<2->pandasai) (3.7.1)\n", 86 | "Requirement already satisfied: distro<2,>=1.7.0 in /usr/lib/python3/dist-packages (from openai<2->pandasai) (1.7.0)\n", 87 | "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.10/dist-packages (from openai<2->pandasai) (0.27.0)\n", 88 | "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from openai<2->pandasai) (1.3.1)\n", 89 | "Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.10/dist-packages (from openai<2->pandasai) (4.66.2)\n", 90 | "Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.10/dist-packages (from openai<2->pandasai) (4.10.0)\n", 91 | "Requirement already satisfied: annotated-types>=0.4.0 in /usr/local/lib/python3.10/dist-packages (from pydantic<3,>=1->pandasai) (0.6.0)\n", 92 | "Requirement already satisfied: pydantic-core==2.16.3 in /usr/local/lib/python3.10/dist-packages (from pydantic<3,>=1->pandasai) (2.16.3)\n", 93 | "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests<3.0.0,>=2.31.0->pandasai) (3.3.2)\n", 94 | "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests<3.0.0,>=2.31.0->pandasai) (3.6)\n", 95 | "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests<3.0.0,>=2.31.0->pandasai) (2.0.7)\n", 96 | "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests<3.0.0,>=2.31.0->pandasai) (2024.2.2)\n", 97 | "Requirement already satisfied: greenlet!=0.4.17 in /usr/local/lib/python3.10/dist-packages (from sqlalchemy<3,>=1.4->pandasai) (3.0.3)\n", 98 | "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai<2->pandasai) (1.2.0)\n", 99 | "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->openai<2->pandasai) (1.0.5)\n", 100 | "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai<2->pandasai) (0.14.0)\n", 101 | "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.1->pandas==1.5.3->pandasai) (1.16.0)\n" 102 | ] 103 | } 104 | ], 105 | "source": [ 106 | "!pip install pandasai -U" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "source": [ 112 | "\n", 113 | "import os\n", 114 | "from pandasai import SmartDataframe\n", 115 | "\n", 116 | "# By default, unless you choose a different LLM, it will use BambooLLM.\n", 117 | "# You can get your free API key signing up at https://pandabi.ai (you can also configure it in your .env file)\n", 118 | "\n", 119 | "from google.colab import userdata\n", 120 | "\n", 121 | "os.environ[\"PANDASAI_API_KEY\"] = userdata.get('PANDASAI_API_KEY')\n", 122 | "\n", 123 | "# You can instantiate a SmartDataframe with a path to a CSV file\n", 124 | "sdf = SmartDataframe(\"Yulu.csv\", config={\"verbose\": True})\n", 125 | "\n", 126 | "response = sdf.chat(\"How many seasons are included in this dataset?\")\n", 127 | "print(response)" 128 | ], 129 | "metadata": { 130 | "colab": { 131 | "base_uri": "https://localhost:8080/" 132 | }, 133 | "id": "CLNhtpiqjvJR", 134 | "outputId": "6f6163d3-4661-4370-9174-7bf88fbb345f" 135 | }, 136 | "execution_count": 54, 137 | "outputs": [ 138 | { 139 | "output_type": "stream", 140 | "name": "stdout", 141 | "text": [ 142 | "The number of seasons included in this dataset is: 4.\n" 143 | ] 144 | } 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "source": [ 150 | "print(sdf.last_code_executed)" 151 | ], 152 | "metadata": { 153 | "colab": { 154 | "base_uri": "https://localhost:8080/" 155 | }, 156 | "id": "oim8y5JaooiE", 157 | "outputId": "bb858520-a408-4a1d-870b-d25782709663" 158 | }, 159 | "execution_count": 56, 160 | "outputs": [ 161 | { 162 | "output_type": "stream", 163 | "name": "stdout", 164 | "text": [ 165 | "seasons_count = dfs[0]['season'].nunique()\n", 166 | "result = {'type': 'string', 'value': f'The number of seasons included in this dataset is: {seasons_count}.'}\n" 167 | ] 168 | } 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "source": [ 174 | "response = sdf.chat(\"Plot the comparison between registered and casual users on seasons level in 2011\")\n", 175 | "print(response)" 176 | ], 177 | "metadata": { 178 | "colab": { 179 | "base_uri": "https://localhost:8080/", 180 | "height": 582 181 | }, 182 | "id": "a4DJIycrkhHd", 183 | "outputId": "2d902dbc-bdad-4f19-b41e-4b5c6b769c6b" 184 | }, 185 | "execution_count": 57, 186 | "outputs": [ 187 | { 188 | "output_type": "display_data", 189 | "data": { 190 | "text/plain": [ 191 | "
" 192 | ], 193 | "image/png": "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QIBcXV1Vv359fffdd9nq59dff9U777yj1q1bZwprkuTo6KiXXnrJPLt28uRJPffcc6pUqZJcXV1VokQJPfnkk5kuobrd+ynd/F6g7t27y8/Pz25ado/xu2X37t0KCQlRyZIl5erqKn9/f/Xs2dOuJjc+g1FRUXrooYfk4eGhIkWKqFKlSnrllVey3eeSJUtUqVIl8+fqli1bzHkbN26UzWbTF198kWm5pUuXymazKTo6+qbrPn78uMqUKZMprEmSp6dnpmnffPONmjRposKFC6to0aIKDQ3VoUOH7Gr279+v7t27m5fie3t7q2fPnvrzzz/t6qz08/jGz21Oj9UbNWrUKFMIrFixoqpVq6bDhw/bTf/ss8/Uvn17M6xJUnBwsB544AF98skndrU3HkM3U7x4cRUtWjRbtcCNOMMG3CNff/21ypcvr0aNGmWrvnfv3lq8eLGeeOIJvfjii9qxY4cmT56sw4cPZ/pF4NixY3rmmWfUr18//fe//9W0adPUoUMHLViwQK+88oqee+45SdLkyZP11FNPZbpMLi0tTW3btlXDhg01depURUZGauzYsbp27ZomTJhg1s2ePVuPPPKIunbtqtTUVC1btkxPPvmkVq1apdDQULuevv32W33yyScaNGiQSpYsedP/qfXv31+ffvqpBg0apKpVq+rPP//U1q1bdfjwYdWtW1eSFBERoR49eujBBx/U5MmTdebMGc2ePVvff/+99u3bZ3emKy0tTSEhIWrQoIGmTZum9evXa/r06QoICNCAAQNuuc/HjRun8ePHKzg4WAMGDFBsbKzmz5+vXbt26fvvv1fBggU1a9YsffDBB/riiy/Myxxr1qx52/fzRhm/eBcrVsxun7Vr106BgYEaO3asHBwctGjRIrVs2VLfffedeSZu3759atu2rUqXLq3x48crLS1NEyZMuOVfd69/jZMnT1bv3r1Vv359JSUlaffu3dq7d69at26tfv366ffff1dUVJQ+/PDDTMv369fPfD8GDx6suLg4zZkzR/v27TP3UYbY2Fh16dJF/fr1U58+fVSpUiVdunRJzZo102+//aZ+/fqpbNmy2rZtm0aOHKnTp09r1qxZkv4+G/Loo49q69at6t+/v6pUqaIvvvhCYWFh2dq/P//8s1auXKknn3xS/v7+OnPmjN555x01a9ZMP/74o3x8fOzqp0yZ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GXR1ddWWLVu0ceNGrV69WpGRkVq+fLlatmypdevW5drPkG7duumFF17Qr7/+qpSUFG3fvl1z5szJ0TocHR1Vo0YN1ahRQ0FBQWrRooWWLFmi4OBg83Pw4YcfytvbO9Oy1399ylNPPaVt27Zp2LBhql27trkv27Zta/d5uh9+Ht/psZqYmKh27drp/Pnz+u677zLdz5oxKNjp06czLXv69Gnz+xOBe4nABtwj7du318KFCxUdHX3LG86lv286T09P19GjR1WlShVz+pkzZ3T+/PksRw+7E+np6fr555/tflnO+DLRjJvWP/vsM7m4uGjt2rV2/7NatGjRHW+/dOnSeu655/Tcc88pISFBdevW1euvv6527dqZrzU2NjbTDfixsbG5ti+u3871Z0xSU1MVFxdnngHKDUWKFNHYsWPVo0cPffLJJ+rcubMCAgIk/R3ub7UtT09Pubi4ZDkqWnZGSpP+Hq2sR48e6tGjhy5evKimTZtq3Lhxdr/gZyUgIEDr169X48aN//FfmAMCAnTx4sXb7s9y5cppw4YNunjxot1ZttjY2Gxt59NPP1WLFi303nvv2U0/f/68GZxzIuPzcfToUbvP4dWrVxUXF6datWrdcvl27drJ0dFRH330UbYGHvn0008VFham6dOnm9OuXLmS5SiZt3s/ixUrluXlYjeeDcntY7xUqVIqVKjQTd+z2NhYFSpUKNP70bBhQzVs2FCvv/66li5dqq5du2rZsmXq3bt3rnwGpb+vKmjVqpVatWqlGTNmaNKkSXr11Ve1cePG2342s7r89KefflKhQoXsAmPnzp0VHh6ujz/+WJcvX1bBggX19NNP/+OeMwbByAgSGT8zPD09b9nzuXPntGHDBo0fP15jxoy55euQrPHz+G65cuWKOnTooJ9++knr16/P8rL9//znPypVqlSWX2S/c+fObP3BBchtXBIJ3CPDhw9X4cKF1bt3b505cybT/OPHj2v27NmSpIcffliSzBHzMsyYMUOS7spobdf/5dcwDM2ZM0cFCxZUq1atJP39V02bzWb3V/kTJ05o5cqV/3ibaWlpmS618vT0lI+Pj3k5Sr169eTp6akFCxbYXaLyzTff6PDhw7m2L4KDg+Xk5KS33nrL7i+17733nhITE3N9n3ft2lVlypTRG2+8IUkKDAxUQECApk2bposXL2aqz7jUytHR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cl1qamqurKdUqVIqVKhQrqwru3KrdwAAACA3ENhwx5o3b65BgwZpyJAhKlmypEJCQnTw4EG1a9dORYoUkZeXl5599ln98ccf5jIXLlxQ165dVbhwYZUuXVozZ85U8+bN7S4nvP6smWEYGjdunMqWLStnZ2f5+Pho8ODB5vZPnjypoUOHymazyWazmevYunWrmjRpIldXV/n6+mrw4MFKTk6228bEiRPVrVs3ubm5mWf0brdcQkKCOnToIFdXV/n7+2vJkiV3Y9cCAAAgnyOwIVcsXrxYTk5O+v777zVlyhS1bNlSderU0e7duxUZGakzZ87oqaeeMuvDw8P1/fff66uvvlJUVJS+++477d2796br/+yzzzRz5ky98847Onr0qFauXKkaNWpIkj7//HOVKVNGEyZM0OnTp3X69GlJ0vHjx9W2bVt16tRJ+/fv1/Lly7V161YNGjTIbt3Tpk1TrVq1tG/fPo0ePTpby3Xv3l2//PKLNm7cqE8//VTz5s1TQkJCbu5SAAAAgEFHkDsqVqyoqVOnSpJee+011alTR5MmTTLnv//++/L19dVPP/2k0qVLa/HixVq6dKlatWolSVq0aJF8fHxuuv5Tp07J29tbwcHBKliwoMqWLav69etLkooXLy5HR0cVLVpU3t7e5jKTJ09W165dzbN2FStW1FtvvaVmzZpp/vz5cnFxkSS1bNlSL774orlc7969b7ncqVOn9M0332jnzp168MEHJUnvvfeeqlSpcod7EQAAALBHYEOuCAwMNP/9ww8/aOPGjSpSpEimuuPHj+vy5cu6evWqGbgkyd3dXZUqVbrp+p988knNmjVL5cuXV9u2bfXwww+rQ4cOKlDg5h/hH374Qfv377e7XNEwDKWnpysuLs4MWPXq1cvRcj/99JMKFChg95orV64sDw+Pm/YCAAAA/BMENuSKwoULm/++ePGiOnTooDfeeCNTXenSpXXs2LEcr9/X11exsbFav369oqKi9Nxzz+nNN9/U5s2bVbBgwSyXuXjxovr162fe63a9smXLZtl7dpb76aefctw/AAAA8E8Q2JDr6tatq88++0x+fn5ZngErX768ChYsqF27dpnBKTExUT/99JOaNm160/W6urqqQ4cO6tChgwYOHKjKlSvrwIEDqlu3rpycnJSWlpapjx9//FEVKlTIcf+3Wq5y5cq6du2a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AAAAFkVgAwAAAACLIrABAAAAgEUR2AAAAADAoghsAAAAAGBRBDYAAAAAsCgCGwAAAABYFIENAAAAACyKwAYAAAAAFkVgAwAAAACLIrABAAAAgEUR2AAAAADAoghsAAAAAGBRBDYAAAAAsCgCGwAAAABYFIENAAAAACyKwAYAAAAAFkVgAwAAAACLIrABAAAAgEUR2AAAAADAoghsAAAAAGBRBDYAAAAAsCgCGwAAAABYFIENAAAAACyKwAYAAAAAFkVgAwAAAACLIrABAAAAgEUR2AAAAADAoghsAAAAAGBRBDYAAAAAsCgCGwAAAABYFIENAAAAACyKwAYAAAAAFkVgAwAAAACLIrABAAAAgEUR2AAAAADAoghsAAAAAGBRBDYAAAAAsCgCGwAAAABYFIENAAAAACyKwAYAAAAAFkVgAwAAAACLIrABAAAAgEUR2AAAAADAovI0sG3ZskUdOnSQj4+PbDabVq5cac67evWqRowYoRo1aqhw4cLy8fFRt27d9Pvvv9utw8/PTzabze4xZcoUu5r9+/erSZMmcnFxka+vr6ZOnZqplxUrVqhy5cpycXFRjRo1tGbNGrv5hmFozJgxKl26tFxdXRUcHKyjR4/m3s4AAAAAgBvkaWBLTk5WrVq1NHfu3EzzLl26pL1792r06NHau3evPv/8c8XGxuqRRx7JVDthwgSdPn3afDz//PPmvKSkJLVp00blypXTnj179Oabb2rcuHFauHChWbNt2zZ16dJFvXr10r59+9SxY0d17NhRBw8eNGumTp2qt956SwsWLNCOHTtUuHBhhYSE6MqVK7m8VwAAAADgbwXycuPt2rVTu3btspzn7u6uqKgou2lz5sxR/fr1derUKZUtW9acXrRoUXl7e2e5niVLlig1NVXvv/++nJycVK1aNcXExGjGjBnq27evJGn27Nlq27athg0bJkmaOHGioqKiNGfOHC1YsECGYWjWrFkaNWqUHn30UUnSBx98IC8vL61cuVKdO3e+430BAAAAADe6r+5hS0xMlM1mk4eHh930KVOmqESJEqpTp47efPNNXbt2zZwXHR2tpk2bysnJyZwWEhKi2NhYnTt3zqwJDg62W2dISIiio6MlSXFxcYqPj7ercXd3V4MGDcyarKSkpCgpKcnuAQAAAADZladn2HLiypUrGjFihLp06SI3Nzdz+uDB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\n" 194 | }, 195 | "metadata": {} 196 | }, 197 | { 198 | "output_type": "stream", 199 | "name": "stdout", 200 | "text": [ 201 | "/content/exports/charts/temp_chart.png\n" 202 | ] 203 | } 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "source": [ 209 | "print(sdf.last_code_executed)" 210 | ], 211 | "metadata": { 212 | "colab": { 213 | "base_uri": "https://localhost:8080/" 214 | }, 215 | "id": "wdN80nExp3sN", 216 | "outputId": "5d92c7e5-49f3-4181-bb22-85c8ca0a2bfa" 217 | }, 218 | "execution_count": 58, 219 | "outputs": [ 220 | { 221 | "output_type": "stream", 222 | "name": "stdout", 223 | "text": [ 224 | "df_2011 = dfs[0][dfs[0]['datetime'].str.startswith('2011')]\n", 225 | "seasonal_data = df_2011.groupby('season')[['registered', 'casual']].sum()\n", 226 | "seasonal_data.plot(kind='bar', figsize=(10, 6))\n", 227 | "plt.title('Comparison of Registered and Casual Users by Season in 2011')\n", 228 | "plt.xlabel('Season')\n", 229 | "plt.ylabel('Total Users')\n", 230 | "plt.xticks(rotation=0)\n", 231 | "plt.legend(title='User Type')\n", 232 | "plt.show()\n", 233 | "result = {'type': 'plot', 'value': '/content/exports/charts/temp_chart.png'}\n" 234 | ] 235 | } 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "source": [ 241 | "## PandasAI with OpenAI" 242 | ], 243 | "metadata": { 244 | "id": "e8XtWEErqDOh" 245 | } 246 | }, 247 | { 248 | "cell_type": "code", 249 | "source": [ 250 | "from pandasai import SmartDataframe\n", 251 | "from pandasai.llm import OpenAI\n", 252 | "from google.colab import userdata\n", 253 | "\n", 254 | "openai_llm = OpenAI(api_token=userdata.get('OPENAI_API_KEY'))\n" 255 | ], 256 | "metadata": { 257 | "id": "XXw1k3HMqVTD" 258 | }, 259 | "execution_count": 59, 260 | "outputs": [] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "source": [ 265 | "openai_llm.chat_completion(\"Who are you?\", memory = None)" 266 | ], 267 | "metadata": { 268 | "colab": { 269 | "base_uri": "https://localhost:8080/", 270 | "height": 36 271 | }, 272 | "id": "iif2zXjCrMrl", 273 | "outputId": "458eb896-c8ed-49e2-f9e8-276942f1c997" 274 | }, 275 | "execution_count": 60, 276 | "outputs": [ 277 | { 278 | "output_type": "execute_result", 279 | "data": { 280 | "text/plain": [ 281 | "'I am a language model AI created by OpenAI. I am here to assist you with any questions or tasks you may have. How can I help you today?'" 282 | ], 283 | "application/vnd.google.colaboratory.intrinsic+json": { 284 | "type": "string" 285 | } 286 | }, 287 | "metadata": {}, 288 | "execution_count": 60 289 | } 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "source": [ 295 | "sdf = SmartDataframe(\"Yulu.csv\", config={\"llm\": openai_llm, \"verbose\": True})" 296 | ], 297 | "metadata": { 298 | "id": "bZoGY5cUrVY5" 299 | }, 300 | "execution_count": 61, 301 | "outputs": [] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "source": [ 306 | "response = sdf.chat(\"Plot the comparison between registered and casual users in the days of week\")\n", 307 | "print(response)" 308 | ], 309 | "metadata": { 310 | "colab": { 311 | "base_uri": "https://localhost:8080/", 312 | "height": 524 313 | }, 314 | "id": "wGwy4duMqkun", 315 | "outputId": "ac394abe-fada-4904-fb73-3b78a5d5d1fe" 316 | }, 317 | "execution_count": 62, 318 | "outputs": [ 319 | { 320 | "output_type": "stream", 321 | "name": "stdout", 322 | "text": [ 323 | "/content/exports/charts/temp_chart.png\n" 324 | ] 325 | }, 326 | { 327 | "output_type": "display_data", 328 | "data": { 329 | "text/plain": [ 330 | "
" 331 | ] 332 | }, 333 | "metadata": {} 334 | }, 335 | { 336 | "output_type": "display_data", 337 | "data": { 338 | "text/plain": [ 339 | "
" 340 | ], 341 | "image/png": "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V66ESqVCWFiYNs2i8PDwwMyZMxETE4M5c+Yo5bdv30bfvn1RokQJmJubI2/evPj666/V9v8bN25ApVJhxowZ6ZZ7+PBhqFQqrFq16p3rj46ORvfu3WFvbw8zMzN4eHhg6dKlyvTU75ebN28iODhY2Xbfdrm9evXqAF4ncqlevnyJkydPolWrVihcuLDatIcPH+LKlSvKfIDm++urV68watQoFC1aVPleGjx4cLpjakbGjx8PAwMDzJ49+711P5rQZyNfvnxSuHBhjev7+voKAGnTpo0EBgZK586dBYC0aNFCrV7BggWlRIkS4ujoKKNHj5YZM2ZIvnz5xMLCQv78808pUKCATJo0SSZNmiRWVlZStGhRSU5OVluPmZmZFCtWTDp16iRz5syRpk2bCgD5+eef1daVP39+6du3r8yZM0emT58ulSpVEgCydetWtXoAxM3NTWxtbWXMmDESGBgop06dUqaNGjVKqfvNN9+IiYmJBAQEyB9//CGTJ0+WZs2ayZ9//qnUWbJkiQCQihUryowZM2To0KFibm4uhQoVkidPnqT7LKVKlZJu3brJvHnzpHXr1gJA5s6d+942HzVqlAAQb29vmT17tvj7+4uhoaFUrFhREhISRERk48aN0rJlSwEg8+bNk+XLl8vp06czXF58fLwYGxtL3bp137vuVJq08blz58TExEQqVKggs2bNkvnz58vAgQOlZs2a6drs5s2basvfu3evAJC9e/cqZd9//700btxYJkyYIL///rt0795dDA0NpU2bNhm2z/toujxN/18pKSlSs2ZNMTAwkL59+8rs2bOlbt26UqZMGQEgS5YseWc8qZ953bp1auVbt24VADJkyBClLCoqSvLnzy/Ozs4yduxYmTdvnjRv3lwAyIwZM5R6z549k8KFC4u5ubkMHTpUZs6cKZUqVRIPD4907evr6ysFCxZU3u/cuVMASL169SQwMFACAwPF399fvv76axERuX79uvzwww8CQIYPHy7Lly+X5cuXS1RUlIiILFu2TFQqlTRs2FBmz54tkydPlkKFCom1tbXa/9vX11dMTU2lSJEi4uvrK/Pnz5dly5aJiEiPHj3EyMhIevbsKfPnz5chQ4ZIzpw51bZ1EZERI0YIAGncuLHMmTNHunXrJk5OTmJjYyO+vr7vbPebN28KAJk6dWqG06dOnaq2jT548EBy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y4iMjFTqEBEREemSTi/F+vn5YeXKldi8eTNy5cql3KtmZWUFc3NzXL9+HStXrkTjxo2RN29enDlzBgMGDEDNmjVRpkwZAECDBg1QsmRJdOrUCVOmTEFUVBRGjBgBPz8/mJqaAgC+++47zJkzB4MHD0a3bt2wZ88erF27FsHBwUosAQEB8PX1RYUKFVCpUiXMnDkT8fHxSi9ZKysrdO/eHQEBAciTJw8sLS3x/fffw8vLiz1iiYiIKFvQaWI3b948AK8HIU5ryZIl6NKlC0xMTLBr1y4lyXJ2dkbr1q0xYsQIpa6hoSG2bt2KPn36wMvLCzlz5oSvr6/auHcuLi4IDg7GgAEDMGvWLOTPnx9//PGHMtQJALRr1w4PHz7EyJEjERUVhbJlyyIkJEStQ8WMGTNgYGCA1q1bqw1QTERERJQd6DSxE3n3I0CcnZ3TPXUiIwULFsS2bdveWad27do4derUO+v4+/vD39//rdPNzMwQGBiIwMDA98ZERERE9Klli84TRERERPTxmNgRERER6QkmdkRERER6gokdERERkZ5gYkdERESkJ5jYEREREekJJnZEREREeoKJHREREZGeYGJHREREpCeY2BERERHpCSZ2RERERHqCiR0RERGRnmBiR0RERKQnmNgRERER6QkmdkRERER6gokdERERkZ5gYkdERESkJ5jYEREREekJJnZEREREeoKJHREREZGeYGJHREREpCeY2BERERHpCSZ2RERERHqCiR0RERGRntA6sVu6dCmCg4OV94MHD4a1tTWqVq2K27dvZ2pwRERERKQ5rRO7CRMmwNzcHAAQFhaGwMBATJkyBTY2NhgwYECmB0hEREREmjHSdoY7d+6gaNGiAIBNmzahdevW6NWrF6pVq4batWtndnxEREREpCGtz9hZWFjg0aNHAICdO3eifv36AAAzMzO8ePEic6MjIiIiIo1pfcaufv366NGjB8qVK4crV66gcePGAIDz58+jUKFCmR0fEREREWlI6zN2gYGBqFq1Kh4+fIgNGzYgb968AIDw8HB06NAh0wMkIiIiIs1odcYuKSkJv/32G4YMGYL8+fOrTRszZkymBkZERERE2tHqjJ2RkRGmTJmCpKSkrIqHiIiIiD6Q1pdi69Wrh3/++ScrYiEiIiKij6B154lGjRph6NChOHv2LDw9PZEzZ0616c2bN8+0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mbU4IiIiItKSxvfYBQQEZFgeGxuLkydP4sqVK9i/f3+mBUZERERE2tE4sTt16lSG5ZaWlqhfvz7++usvuLi4ZFpgRERERKQdjRO7vXv3ZmUcRERERPSRMu0eOyIiIiLSLSZ2RERERHqCiR0RERGRnmBiR0RERKQnNErsypcvjydPngAAxo4di+fPn2dpUERERESkPY0Su4sXLyI+Ph4AMGbMGDx79ixLgyIiIiIi7Wk03EnZsmXRtWtXVK9eHSKCX3/9FRYWFhnWHTlyZKYGSERERESa0SixCwoKwqhRo7B161aoVCps374dRkbpZ1WpVEzsiIiIiHREo8SuRIkSWL16NQDAwMAAu3fvhp2dXZYGRkRERETa0fjJE6lSUlKyIg4iIiIi+khaJ3YAcP36dcycORMXL14EAJQsWRL9+vVDkSJFMjU4IiIiItKc1uPY7dixAyVLlsSxY8dQpkwZlClTBkePHkWpUqUQGhqaFTESERERkQa0PmM3dOhQDBgwAJMmTUpXPmTIENSvXz/TgiMiIiIizWl9xu7ixYvo3r17uvJu3brhwoULmRIUEREREWlP68TO1tYWERER6cojIiLYU5aIiIhIh7S+FNuzZ0/06tULN27cQNWqVQEAhw4dwuTJkxEQEJDpARIRERGRZrRO7H7++WfkypUL06ZNw7BhwwAATk5OGD16NH744YdMD5CIiIiINKN1YqdSqTBgwAAMGDAAT58+BQDkypUr0wMjIiIiIu180Dh2qZjQEREREWUfWneeICIiIqLsiYkdERERkZ5gYkdERESkJ7RK7BITE1GvXj1cvXo1q+IhIiIiog+kVWJnbGyMM2fOZFUsRERERPQRtL4U++2332LRokVZEQsRERERfQSthztJSkrC4sWLsWvXLnh6eiJnzpxq06dPn55pwRERERGR5rRO7M6dO4fy5csDAK5cuaI2TaVSZU5URERERKQ1rRO7vXv3ZkUcRERERPSRPni4k2vXrmHHjh148eIFAEBEtF7G/v370axZMzg5OUGlUmHTpk1q07t06QKVSqX2atiwoVqdx48fo2PHjrC0tIS1tTW6d++OZ8+eqdU5c+YMatSoATMzMzg7O2PKlCnpYlm3bh1cXV1hZmYGd3d3bNu2TW26iGDkyJFwdHSEubk5vL292TuYiIiIshWtE7tHjx6hXr16KF68OBo3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9gzXr19Hp06d4OnpCWNjY+zevRutW7cGAFy+fBmRkZHw8vICAHh5eeGXX35BdHQ07OzsAAChoaGwtLREyZIllTrbtm1TW09oaKiyDBMTE3h6emL37t1o0aIFACAlJQW7d++Gv7//p/jYb1Xo5Uqdrj8jt3QdABER0RcsWyd2AwcORLNmzVCwYEHcu3cPo0aNgqGhITp06AArKyt0794dAQEByJMnDywtLfH999/Dy8sLVapUAQA0aNAAJUuWRKdOnTBlyhRERUVhxIgR8PPzg6mpKQDgu+++w5w5czB48GB069YNe/bswdq1axEcHKzEERAQAF9fX1SoUAGVKlXCzJkzER8fj65du+qkXUh72TEJBpgIExFR5srWid2///6LDh064NGjR7C1tUX16tVx5MgR2NraAgBmzJgBAwMDtG7dGq9evYKPjw/mzp2rzG9oaIitW7eiT58+8PLyQs6cOeHr64uxY8cqdVxcXBAcHIwBAwZg1qxZyJ8/P/744w/4+Pgoddq1a4eHDx9i5MiRiIqKQtmyZRESEpKuQwURERGRLmXrxG716tXvnG5mZobAwEAEBga+tU7BggXTXWp9U+3atXHq1Kl31vH399f5pVciIiKid/msOk8QERER0dsxsSMiIiLSE0zsiIiIiPQEEzsiIiIiPcHEjoiIiEhPMLEjIiIi0hPZergTIiLSL9lxsPBbug6AKBPxjB0RERGRnuAZOyIiomwoO57dBLLnGU621f/wjB0RERGRnmBiR0RERKQnmNgRERER6QkmdkRERER6gokdERERkZ5gYkdERESkJ5jYEREREekJJnZEREREeoKJHREREZGeYGJHREREpCeY2BERERHpCSZ2RERERHqCiR0RERGRnmBiR0RERKQnmNgRERER6QkmdkRERER6gokdERERkZ5gYkdERESkJ5jYEREREekJJnZEREREeoKJHREREZGeYGJHREREpCeY2BERERHpCSZ2RERERHqCiR0RERGRnmBiR0RERKQnmNgRERER6QkmdkRERER6gokdERERkZ5gYkdERESkJ5jYaSkwMBCFChWCmZkZKleujGPHjuk6JCIiIiIATOy0smbNGgQEBGDUqFE4efIkPDw84OPjg+joaF2HRkRERMTEThvTp09Hz5490bVrV5QsWRLz589Hjhw5sHjxYl2HRkRERMTETlMJ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Qx3AS7FE1M53332H/Px8uLu7Iz09Xbnh3dxPhPRgOTg4YM6cOXjssccQGhpq6upQD+bm5oZ58+YhPT0dubm5MBgM2LRpE06ePInExEQsW7YMQOtgik8++QRLliwxcY0fPl6KJSIjt2/fhqOjI1QqlVnfh0Kmw8YC3Y/i4mJ4eHgof9++fRu/+c1vEBQUhMjISEybNg2TJ0/G0aNHISKora3FvHnzcPfuXWRkZFjcOYzBjog61X7UGRGRKRw5cgRhYWGYPn06du3ahb59+8LR0RHZ2dkIDg7Gtm3b4OzsjPnz52PatGkAgIaGBlRVVeGrr76CWq22uAYqgx0RERF1S4WFhQgJCUF1dTW0Wi18fHzw1FNPwdPTE8uXL4der0daWhpqa2uxf/9+iAjc3NywYsUKi72VhMGOiIiIuo226W+am5thMBiQmpqKmpoaODo64vr168jMzERiYiJ69eqFJUuWYMWKFYiNjTUa+QrA4nrq2nBULBEREXUbN27cANA6etXW1haenp44efIkJkyYgJ07d2LNmjWIjo7GuXPn4OLigr/+9a8oLi42CnUALDLUAQx2RERE1E0UFBRgyJAhWLduHS5evAgACAwMhFarxbx581BaWoolS5bg8OHD+O6772BnZ4eqqiq89tprJq5598FLsURERNQt3L59G++99x62bNmCkSNHIigoCBs2bAAALFq0CBqNBlu3bkWfPn1QWVmJy5cv491330VycrLF3UvXFQY7IiIi6la+/fZbJCQkICcnBy4uLti5cyfOnj2LvLw8LFu2DN7e3h1G7lviQInOMNgRERFRt1NdXY2zZ88iLi4O5eXleOqpp3DixAlMnToVu3btMnX1ui0GOyIiIurWNm7ciPPnzyM3NxfV1dU4dOgQZs6caepqdUsMdkRERNQttU19AgCnT5/G0aNHkZGRgby8PF527QKDHREREXVbXT0Fh/fUdY7BjoiIiHoUPvKwa5zHjoiIiHoUhrquMdgRERERmQkGOyIiIiIzwWBHREREZCYY7IiIiIjMBIMdERERkZlgsCMiIiIyEwx2RERERGaCwY6IyMTy8/MxevRoqNXqbvf8y8cffxwpKSmmrgYR3SMGOyIya4sWLYJKpYJKpYJarcYvfvELTJs2DW+//TZaWlpMXT0AwB//+Ed4enriypUreOeddzosj4uLg4eHh1FZ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\n" 342 | }, 343 | "metadata": {} 344 | } 345 | ] 346 | }, 347 | { 348 | "cell_type": "code", 349 | "source": [ 350 | "print(sdf.last_code_executed)" 351 | ], 352 | "metadata": { 353 | "colab": { 354 | "base_uri": "https://localhost:8080/" 355 | }, 356 | "id": "H_f51PMOsGql", 357 | "outputId": "c1ff40c4-758d-45ce-dc14-87a1f514eb3f" 358 | }, 359 | "execution_count": 63, 360 | "outputs": [ 361 | { 362 | "output_type": "stream", 363 | "name": "stdout", 364 | "text": [ 365 | "for df in dfs:\n", 366 | " df['datetime'] = pd.to_datetime(df['datetime'])\n", 367 | " df['day_of_week'] = df['datetime'].dt.day_name()\n", 368 | "grouped_data = pd.concat(dfs).groupby('day_of_week')[['casual', 'registered']].sum()\n", 369 | "plt.figure(figsize=(10, 6))\n", 370 | "grouped_data.plot(kind='bar', stacked=True)\n", 371 | "plt.title('Comparison of Casual and Registered Users by Day of Week')\n", 372 | "plt.xlabel('Day of Week')\n", 373 | "plt.ylabel('Number of Users')\n", 374 | "plt.legend(title='User Type')\n", 375 | "plt.xticks(rotation=45)\n", 376 | "plt.tight_layout()\n", 377 | "plt.savefig('/content/exports/charts/temp_chart.png')\n", 378 | "result = {'type': 'plot', 'value': '/content/exports/charts/temp_chart.png'}\n" 379 | ] 380 | } 381 | ] 382 | } 383 | ] 384 | } --------------------------------------------------------------------------------