├── .gitignore ├── README.md ├── examples ├── Backtranslation_of_SQL_queries.py ├── Classification_using_embeddings.ipynb ├── Clustering.ipynb ├── Clustering_for_transaction_classification.ipynb ├── Code_search.ipynb ├── Customizing_embeddings.ipynb ├── Fine-tuned_classification.ipynb ├── Get_embeddings.ipynb ├── How_to_count_tokens_with_tiktoken.ipynb ├── How_to_handle_rate_limits.ipynb ├── How_to_stream_completions.ipynb ├── Multiclass_classification_for_transactions.ipynb ├── Obtain_dataset.ipynb ├── Question_answering_using_embeddings.ipynb ├── Recommendation_using_embeddings.ipynb ├── Regression_using_embeddings.ipynb ├── Semantic_text_search_using_embeddings.ipynb ├── Unit_test_writing_using_a_multi-step_prompt.ipynb ├── User_and_product_embeddings.ipynb ├── Visualizing_embeddings_in_2D.ipynb ├── Visualizing_embeddings_in_3D.ipynb ├── Zero-shot_classification_with_embeddings.ipynb ├── azure │ ├── completions.ipynb │ ├── embeddings.ipynb │ └── finetuning.ipynb ├── book_translation │ ├── data │ │ ├── geometry_English.tex │ │ └── geometry_slovenian.tex │ └── translate_latex_book.ipynb ├── dalle │ └── Image_generations_edits_and_variations_with_DALL-E.ipynb ├── data │ ├── 25000_spend_dataset_current.csv │ ├── AG_news_samples.csv │ ├── dbpedia_samples.jsonl │ ├── fine_food_reviews_1k.csv │ ├── fine_food_reviews_with_embeddings_1k.csv │ ├── labelled_transactions.csv │ ├── library_transactions_with_embeddings_359.csv │ ├── recommendations_embeddings_cache.pkl │ └── snli_1.0_train_2k.csv └── fine-tuned_qa │ ├── answers_with_ft.py │ ├── olympics-1-collect-data.ipynb │ ├── olympics-2-create-qa.ipynb │ └── olympics-3-train-qa.ipynb ├── images ├── chain_of_thought_fig1.png ├── chain_of_thought_fig11.png ├── chain_of_thought_fig3.png ├── chain_of_thought_fig5.png ├── faithful-reasoning_fig1.png ├── faithful-reasoning_fig2.png ├── faithful-reasoning_fig3.png ├── faithful-reasoning_fig4.png ├── faithful-reasoning_fig5.png ├── faithful-reasoning_fig7.png ├── faithful-reasoning_tab2.png ├── faithful-reasoning_tab5.png ├── least-to-most_fig1.png ├── least-to-most_tab11.png ├── least-to-most_tab4.png ├── least-to-most_tab9.png ├── lm_cascades_fig1.png ├── lm_cascades_fig3.png ├── lm_cascades_fig4.png ├── lm_cascades_fig5.png ├── lm_cascades_fig6.png ├── maieutic_fig2.png ├── maieutic_fig6.png ├── maieutic_tab1.png ├── selection-inference_fig1.png ├── selection-inference_fig4.png ├── self-consistency_fig1.png ├── self-consistency_fig3.png ├── star_fig1.png ├── star_tab1.png ├── verifiers_fig3.png ├── verifiers_fig5.png ├── zero-shot_reasoners_fig1.png ├── zero-shot_reasoners_fig2.png ├── zero-shot_reasoners_tab1.png └── zero-shot_reasoners_tab5.png ├── techniques_to_improve_reliability.md └── transition_guides_for_deprecated_API_endpoints ├── README.md ├── answers_functionality_example.py ├── classification_functionality_example.py └── search_functionality_example.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Data 132 | *transactions*.jsonl 133 | /examples/data/transactions* 134 | *.DS_Store 135 | -------------------------------------------------------------------------------- /examples/Backtranslation_of_SQL_queries.py: -------------------------------------------------------------------------------- 1 | from typing import List, Union 2 | 3 | from smokey import Smokey 4 | 5 | import openai 6 | 7 | 8 | def get_candidates( 9 | prompt: str, 10 | stop: List[str], 11 | temperature: float, 12 | priming_prefix: str, 13 | engine: str, 14 | n: int = 5, 15 | ) -> List[str]: 16 | """ 17 | Generate N candidate completions based on the prompt, generated with a specific temperature. 18 | 19 | :param prompt: The prompt to start the conversation with. 20 | :param stop: A list of tokens that indicate the end of the generation. 21 | :param temperature: The temperature of the generation. 22 | :param priming_prefix: The prefix to use for the priming. 23 | :param engine: The engine to use for the generation. 24 | :param n: The number of completions to generate. 25 | :return: A list of completions. 26 | """ 27 | response = openai.Completion.create( 28 | engine=engine, 29 | prompt=prompt, 30 | temperature=temperature, 31 | max_tokens=150, 32 | top_p=1, 33 | frequency_penalty=0, 34 | presence_penalty=0, 35 | stop=stop, 36 | n=n, 37 | ) 38 | responses = [priming_prefix + choice.text for choice in response.choices] 39 | return responses 40 | 41 | 42 | def rindex(lst: List, value: str) -> int: 43 | """ 44 | Return the index of the last occurence of a value in a list. 45 | 46 | :param lst: The list to search in. 47 | :param value: The value to search for. 48 | :return: The index of the last occurence of the value. 49 | """ 50 | try: 51 | return len(lst) - lst[::-1].index(value) - 1 52 | except ValueError: 53 | raise ValueError(f"Answer start token `{value}` not found in the eval template") 54 | 55 | 56 | def eval_candidate( 57 | candidate_answer: str, 58 | original_instruction: str, 59 | eval_template: str, 60 | answer_start_token: str, 61 | engine: str, 62 | ) -> float: 63 | """ 64 | Evaluate a candidate answer by calculating the average log probability 65 | of the original instruction, given the candidate answer with a specific 66 | evaluation template, aimed at reconstructing the original instruction. 67 | 68 | :param candidate_answer: The candidate answer to evaluate. 69 | :param original_instruction: The original instruction. 70 | :param eval_template: The template to use for the evaluation. 71 | :param answer_start_token: The token to use to indicate the start of the answer. 72 | :param engine: The engine to use for the evaluation. 73 | :return: The evaluation of the candidate answer. 74 | """ 75 | response = openai.Completion.create( 76 | engine=engine, 77 | prompt=eval_template.format(candidate_answer, original_instruction), 78 | temperature=0, 79 | max_tokens=0, 80 | top_p=1, 81 | frequency_penalty=0, 82 | presence_penalty=0, 83 | logprobs=1, 84 | echo=True, 85 | ) 86 | 87 | answer_start = rindex( 88 | response["choices"][0]["logprobs"]["tokens"], answer_start_token 89 | ) 90 | logprobs = response["choices"][0]["logprobs"]["token_logprobs"][answer_start + 1 :] 91 | return sum(logprobs) / len(logprobs) 92 | 93 | 94 | def backtranslation( 95 | prompt_template: str, 96 | additional_info: str, 97 | instruction: str, 98 | eval_template: str, 99 | priming_prefix: str = "SELECT", 100 | stop1: List[str] = ["#", ";"], 101 | answer_start_token: str = "--", 102 | n: int = 5, 103 | temperature: float = 0.5, 104 | return_all_results: bool = False, 105 | engine: str = "davinci-codex", 106 | ) -> Union[str, List[str, float]]: 107 | """ 108 | Generate a number of SQL queries given a natural language instruction, 109 | and pick the best one based on the average log probability of explaining the 110 | candidate SQL query with the exact original instruction, when prompted for 111 | a natural language explanation of the candidate SQL query. 112 | 113 | :param prompt_template: The template to use for the prompt to generate SQL. 114 | :param additional_info: Additional information to include in the prompt 115 | (SQL Tables, and their properties). 116 | :param instruction: The instruction in natural language. 117 | :param eval_template: The template to use for the evaluation. 118 | :param priming_prefix: The prefix to use for the priming of the SQL query. 119 | :param stop1: A list of tokens that indicate the end of the generation. 120 | :param answer_start_token: The token to use to indicate the start of the 121 | natural answer. 122 | :param n: The number of candidates to generate. 123 | :param temperature: The temperature of the generation. 124 | :param return_all_results: Whether to return all results or just the best one. 125 | :param engine: The engine to use for the generation and evaluation. 126 | :return: The best SQL query, or a list of all scored generated SQL queries. 127 | """ 128 | prompt_template = prompt_template.format( 129 | additional_info, instruction, priming_prefix 130 | ) 131 | 132 | candidates = [] 133 | responses = get_candidates( 134 | prompt_template, stop1, temperature, priming_prefix, engine=engine, n=n 135 | ) 136 | for i in range(n): 137 | quality = eval_candidate( 138 | responses[i], 139 | instruction, 140 | eval_template, 141 | answer_start_token, 142 | engine=engine, 143 | ) 144 | candidates.append((responses[i], quality)) 145 | 146 | candidates.sort(key=lambda x: x[1], reverse=True) 147 | if return_all_results: 148 | return candidates 149 | return candidates[0][0] 150 | 151 | 152 | def main( 153 | nl_query: str = "Return the name of each department that had more than 10 employees in June 2021", 154 | eval_template: str = "{};\n-- Explanation of the above query in human readable format\n-- {}", 155 | table_definitions: str = "# Employee(id, name, department_id)\n# Department(id, name, address)\n# Salary_Payments(id, employee_id, amount, date)\n", 156 | prompt_template: str = "### Postgres SQL tables, with their properties:\n#\n{}#\n### {}\n{}", 157 | n: int = 3, 158 | temperature: float = 0.3, 159 | engine: str = "davinci-codex", 160 | ): 161 | """ 162 | Generate a number of SQL queries given a natural language instruction, 163 | and pick the best one based on the highest backtranslation score. 164 | 165 | :param nl_query: The natural language query. 166 | :param eval_template: The template to use for the evaluation. 167 | :param table_definitions: The definitions of the tables used in the query. 168 | :param prompt_template: The template to use for the prompt to generate SQL. 169 | :param n: The number of candidates to generate. 170 | :param temperature: The temperature of the generation. 171 | :param engine: The engine to use for the generation and evaluation. 172 | :return: The best SQL query, or a list of all scored generated SQL queries. 173 | """ 174 | 175 | result = backtranslation( 176 | prompt_template, 177 | table_definitions, 178 | nl_query, 179 | eval_template, 180 | priming_prefix="SELECT", 181 | temperature=temperature, 182 | n=n, 183 | engine=engine, 184 | ) 185 | print(result) 186 | 187 | 188 | if __name__ == "__main__": 189 | Smokey(main) 190 | -------------------------------------------------------------------------------- /examples/Code_search.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "## Code search\n", 9 | "\n", 10 | "We index our own [openai-python code repository](https://github.com/openai/openai-python), and show how it can be searched. We implement a simple version of file parsing and extracting of functions from python files." 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 1, 16 | "metadata": {}, 17 | "outputs": [ 18 | { 19 | "name": "stdout", 20 | "output_type": "stream", 21 | "text": [ 22 | "Total number of py files: 51\n", 23 | "Total number of functions extracted: 97\n" 24 | ] 25 | } 26 | ], 27 | "source": [ 28 | "import os\n", 29 | "from glob import glob\n", 30 | "import pandas as pd\n", 31 | "\n", 32 | "def get_function_name(code):\n", 33 | " \"\"\"\n", 34 | " Extract function name from a line beginning with \"def \"\n", 35 | " \"\"\"\n", 36 | " assert code.startswith(\"def \")\n", 37 | " return code[len(\"def \"): code.index(\"(\")]\n", 38 | "\n", 39 | "def get_until_no_space(all_lines, i) -> str:\n", 40 | " \"\"\"\n", 41 | " Get all lines until a line outside the function definition is found.\n", 42 | " \"\"\"\n", 43 | " ret = [all_lines[i]]\n", 44 | " for j in range(i + 1, i + 10000):\n", 45 | " if j < len(all_lines):\n", 46 | " if len(all_lines[j]) == 0 or all_lines[j][0] in [\" \", \"\\t\", \")\"]:\n", 47 | " ret.append(all_lines[j])\n", 48 | " else:\n", 49 | " break\n", 50 | " return \"\\n\".join(ret)\n", 51 | "\n", 52 | "def get_functions(filepath):\n", 53 | " \"\"\"\n", 54 | " Get all functions in a Python file.\n", 55 | " \"\"\"\n", 56 | " whole_code = open(filepath).read().replace(\"\\r\", \"\\n\")\n", 57 | " all_lines = whole_code.split(\"\\n\")\n", 58 | " for i, l in enumerate(all_lines):\n", 59 | " if l.startswith(\"def \"):\n", 60 | " code = get_until_no_space(all_lines, i)\n", 61 | " function_name = get_function_name(code)\n", 62 | " yield {\"code\": code, \"function_name\": function_name, \"filepath\": filepath}\n", 63 | "\n", 64 | "\n", 65 | "# get user root directory\n", 66 | "root_dir = os.path.expanduser(\"~\")\n", 67 | "# note: for this code to work, the openai-python repo must be downloaded and placed in your root directory\n", 68 | "\n", 69 | "# path to code repository directory\n", 70 | "code_root = root_dir + \"/openai-python\"\n", 71 | "\n", 72 | "code_files = [y for x in os.walk(code_root) for y in glob(os.path.join(x[0], '*.py'))]\n", 73 | "print(\"Total number of py files:\", len(code_files))\n", 74 | "\n", 75 | "if len(code_files) == 0:\n", 76 | " print(\"Double check that you have downloaded the openai-python repo and set the code_root variable correctly.\")\n", 77 | "\n", 78 | "all_funcs = []\n", 79 | "for code_file in code_files:\n", 80 | " funcs = list(get_functions(code_file))\n", 81 | " for func in funcs:\n", 82 | " all_funcs.append(func)\n", 83 | "\n", 84 | "print(\"Total number of functions extracted:\", len(all_funcs))" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 2, 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "data": { 94 | "text/html": [ 95 | "
\n", 96 | "\n", 109 | "\n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | "
codefunction_namefilepathcode_embedding
0def _console_log_level():\\n if openai.log i..._console_log_level/openai/util.py[0.03389773145318031, -0.004390408284962177, 0...
1def log_debug(message, **params):\\n msg = l...log_debug/openai/util.py[-0.004034275189042091, 0.004895383026450872, ...
2def log_info(message, **params):\\n msg = lo...log_info/openai/util.py[0.004882764536887407, 0.0033515947870910168, ...
3def log_warn(message, **params):\\n msg = lo...log_warn/openai/util.py[0.002535992069169879, -0.010829543694853783, ...
4def logfmt(props):\\n def fmt(key, val):\\n ...logfmt/openai/util.py[0.016732551157474518, 0.017367802560329437, 0...
\n", 157 | "
" 158 | ], 159 | "text/plain": [ 160 | " code function_name \\\n", 161 | "0 def _console_log_level():\\n if openai.log i... _console_log_level \n", 162 | "1 def log_debug(message, **params):\\n msg = l... log_debug \n", 163 | "2 def log_info(message, **params):\\n msg = lo... log_info \n", 164 | "3 def log_warn(message, **params):\\n msg = lo... log_warn \n", 165 | "4 def logfmt(props):\\n def fmt(key, val):\\n ... logfmt \n", 166 | "\n", 167 | " filepath code_embedding \n", 168 | "0 /openai/util.py [0.03389773145318031, -0.004390408284962177, 0... \n", 169 | "1 /openai/util.py [-0.004034275189042091, 0.004895383026450872, ... \n", 170 | "2 /openai/util.py [0.004882764536887407, 0.0033515947870910168, ... \n", 171 | "3 /openai/util.py [0.002535992069169879, -0.010829543694853783, ... \n", 172 | "4 /openai/util.py [0.016732551157474518, 0.017367802560329437, 0... " 173 | ] 174 | }, 175 | "execution_count": 2, 176 | "metadata": {}, 177 | "output_type": "execute_result" 178 | } 179 | ], 180 | "source": [ 181 | "from openai.embeddings_utils import get_embedding\n", 182 | "\n", 183 | "df = pd.DataFrame(all_funcs)\n", 184 | "df['code_embedding'] = df['code'].apply(lambda x: get_embedding(x, engine='text-embedding-ada-002'))\n", 185 | "df['filepath'] = df['filepath'].apply(lambda x: x.replace(code_root, \"\"))\n", 186 | "df.to_csv(\"data/code_search_openai-python.csv\", index=False)\n", 187 | "df.head()" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 3, 193 | "metadata": {}, 194 | "outputs": [ 195 | { 196 | "name": "stdout", 197 | "output_type": "stream", 198 | "text": [ 199 | "/openai/tests/test_endpoints.py:test_completions score=0.826\n", 200 | "def test_completions():\n", 201 | " result = openai.Completion.create(prompt=\"This was a test\", n=5, engine=\"ada\")\n", 202 | " assert len(result.choices) == 5\n", 203 | "\n", 204 | "\n", 205 | "----------------------------------------------------------------------\n", 206 | "/openai/tests/test_endpoints.py:test_completions_model score=0.811\n", 207 | "def test_completions_model():\n", 208 | " result = openai.Completion.create(prompt=\"This was a test\", n=5, model=\"ada\")\n", 209 | " assert len(result.choices) == 5\n", 210 | " assert result.model.startswith(\"ada\")\n", 211 | "\n", 212 | "\n", 213 | "----------------------------------------------------------------------\n", 214 | "/openai/tests/test_endpoints.py:test_completions_multiple_prompts score=0.808\n", 215 | "def test_completions_multiple_prompts():\n", 216 | " result = openai.Completion.create(\n", 217 | " prompt=[\"This was a test\", \"This was another test\"], n=5, engine=\"ada\"\n", 218 | " )\n", 219 | " assert len(result.choices) == 10\n", 220 | "\n", 221 | "\n", 222 | "----------------------------------------------------------------------\n" 223 | ] 224 | } 225 | ], 226 | "source": [ 227 | "from openai.embeddings_utils import cosine_similarity\n", 228 | "\n", 229 | "def search_functions(df, code_query, n=3, pprint=True, n_lines=7):\n", 230 | " embedding = get_embedding(code_query, engine='text-embedding-ada-002')\n", 231 | " df['similarities'] = df.code_embedding.apply(lambda x: cosine_similarity(x, embedding))\n", 232 | "\n", 233 | " res = df.sort_values('similarities', ascending=False).head(n)\n", 234 | " if pprint:\n", 235 | " for r in res.iterrows():\n", 236 | " print(r[1].filepath+\":\"+r[1].function_name + \" score=\" + str(round(r[1].similarities, 3)))\n", 237 | " print(\"\\n\".join(r[1].code.split(\"\\n\")[:n_lines]))\n", 238 | " print('-'*70)\n", 239 | " return res\n", 240 | "\n", 241 | "res = search_functions(df, 'Completions API tests', n=3)" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": 4, 247 | "metadata": {}, 248 | "outputs": [ 249 | { 250 | "name": "stdout", 251 | "output_type": "stream", 252 | "text": [ 253 | "/openai/validators.py:format_inferrer_validator score=0.751\n", 254 | "def format_inferrer_validator(df):\n", 255 | " \"\"\"\n", 256 | " This validator will infer the likely fine-tuning format of the data, and display it to the user if it is classification.\n", 257 | " It will also suggest to use ada and explain train/validation split benefits.\n", 258 | " \"\"\"\n", 259 | " ft_type = infer_task_type(df)\n", 260 | " immediate_msg = None\n", 261 | "----------------------------------------------------------------------\n", 262 | "/openai/validators.py:get_validators score=0.748\n", 263 | "def get_validators():\n", 264 | " return [\n", 265 | " num_examples_validator,\n", 266 | " lambda x: necessary_column_validator(x, \"prompt\"),\n", 267 | " lambda x: necessary_column_validator(x, \"completion\"),\n", 268 | " additional_column_validator,\n", 269 | " non_empty_field_validator,\n", 270 | "----------------------------------------------------------------------\n", 271 | "/openai/validators.py:infer_task_type score=0.738\n", 272 | "def infer_task_type(df):\n", 273 | " \"\"\"\n", 274 | " Infer the likely fine-tuning task type from the data\n", 275 | " \"\"\"\n", 276 | " CLASSIFICATION_THRESHOLD = 3 # min_average instances of each class\n", 277 | " if sum(df.prompt.str.len()) == 0:\n", 278 | " return \"open-ended generation\"\n", 279 | "----------------------------------------------------------------------\n" 280 | ] 281 | } 282 | ], 283 | "source": [ 284 | "res = search_functions(df, 'fine-tuning input data validation logic', n=3)" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": 5, 290 | "metadata": {}, 291 | "outputs": [ 292 | { 293 | "name": "stdout", 294 | "output_type": "stream", 295 | "text": [ 296 | "/openai/validators.py:get_common_xfix score=0.793\n", 297 | "def get_common_xfix(series, xfix=\"suffix\"):\n", 298 | " \"\"\"\n", 299 | " Finds the longest common suffix or prefix of all the values in a series\n", 300 | " \"\"\"\n", 301 | " common_xfix = \"\"\n", 302 | " while True:\n", 303 | " common_xfixes = (\n", 304 | " series.str[-(len(common_xfix) + 1) :]\n", 305 | " if xfix == \"suffix\"\n", 306 | " else series.str[: len(common_xfix) + 1]\n", 307 | "----------------------------------------------------------------------\n", 308 | "/openai/validators.py:common_completion_suffix_validator score=0.778\n", 309 | "def common_completion_suffix_validator(df):\n", 310 | " \"\"\"\n", 311 | " This validator will suggest to add a common suffix to the completion if one doesn't already exist in case of classification or conditional generation.\n", 312 | " \"\"\"\n", 313 | " error_msg = None\n", 314 | " immediate_msg = None\n", 315 | " optional_msg = None\n", 316 | " optional_fn = None\n", 317 | "\n", 318 | " ft_type = infer_task_type(df)\n", 319 | "----------------------------------------------------------------------\n" 320 | ] 321 | } 322 | ], 323 | "source": [ 324 | "res = search_functions(df, 'find common suffix', n=2, n_lines=10)" 325 | ] 326 | }, 327 | { 328 | "cell_type": "code", 329 | "execution_count": 6, 330 | "metadata": {}, 331 | "outputs": [ 332 | { 333 | "name": "stdout", 334 | "output_type": "stream", 335 | "text": [ 336 | "/openai/cli.py:tools_register score=0.773\n", 337 | "def tools_register(parser):\n", 338 | " subparsers = parser.add_subparsers(\n", 339 | " title=\"Tools\", help=\"Convenience client side tools\"\n", 340 | " )\n", 341 | "\n", 342 | " def help(args):\n", 343 | " parser.print_help()\n", 344 | "\n", 345 | " parser.set_defaults(func=help)\n", 346 | "\n", 347 | " sub = subparsers.add_parser(\"fine_tunes.prepare_data\")\n", 348 | " sub.add_argument(\n", 349 | " \"-f\",\n", 350 | " \"--file\",\n", 351 | " required=True,\n", 352 | " help=\"JSONL, JSON, CSV, TSV, TXT or XLSX file containing prompt-completion examples to be analyzed.\"\n", 353 | " \"This should be the local file path.\",\n", 354 | " )\n", 355 | " sub.add_argument(\n", 356 | " \"-q\",\n", 357 | "----------------------------------------------------------------------\n" 358 | ] 359 | } 360 | ], 361 | "source": [ 362 | "res = search_functions(df, 'Command line interface for fine-tuning', n=1, n_lines=20)" 363 | ] 364 | } 365 | ], 366 | "metadata": { 367 | "interpreter": { 368 | "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" 369 | }, 370 | "kernelspec": { 371 | "display_name": "openai-cookbook", 372 | "language": "python", 373 | "name": "openai-cookbook" 374 | }, 375 | "language_info": { 376 | "codemirror_mode": { 377 | "name": "ipython", 378 | "version": 3 379 | }, 380 | "file_extension": ".py", 381 | "mimetype": "text/x-python", 382 | "name": "python", 383 | "nbconvert_exporter": "python", 384 | "pygments_lexer": "ipython3", 385 | "version": "3.9.6" 386 | }, 387 | "orig_nbformat": 4 388 | }, 389 | "nbformat": 4, 390 | "nbformat_minor": 2 391 | } 392 | -------------------------------------------------------------------------------- /examples/Get_embeddings.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Get embeddings\n", 8 | "\n", 9 | "The function `get_embedding` will give us an embedding for an input text." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "data": { 19 | "text/plain": [ 20 | "1536" 21 | ] 22 | }, 23 | "execution_count": 1, 24 | "metadata": {}, 25 | "output_type": "execute_result" 26 | } 27 | ], 28 | "source": [ 29 | "import openai\n", 30 | "\n", 31 | "embedding = openai.Embedding.create(\n", 32 | " input=\"Your text goes here\", model=\"text-embedding-ada-002\"\n", 33 | ")[\"data\"][0][\"embedding\"]\n", 34 | "len(embedding)\n" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 2, 40 | "metadata": {}, 41 | "outputs": [ 42 | { 43 | "name": "stdout", 44 | "output_type": "stream", 45 | "text": [ 46 | "1536\n" 47 | ] 48 | } 49 | ], 50 | "source": [ 51 | "import openai\n", 52 | "from tenacity import retry, wait_random_exponential, stop_after_attempt\n", 53 | "\n", 54 | "\n", 55 | "@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))\n", 56 | "def get_embedding(text: str, model=\"text-embedding-ada-002\") -> list[float]:\n", 57 | " return openai.Embedding.create(input=[text], model=model)[\"data\"][0][\"embedding\"]\n", 58 | "\n", 59 | "\n", 60 | "embedding = get_embedding(\"Your text goes here\", model=\"text-embedding-ada-002\")\n", 61 | "print(len(embedding))\n" 62 | ] 63 | } 64 | ], 65 | "metadata": { 66 | "kernelspec": { 67 | "display_name": "Python 3.9.9 ('openai')", 68 | "language": "python", 69 | "name": "python3" 70 | }, 71 | "language_info": { 72 | "codemirror_mode": { 73 | "name": "ipython", 74 | "version": 3 75 | }, 76 | "file_extension": ".py", 77 | "mimetype": "text/x-python", 78 | "name": "python", 79 | "nbconvert_exporter": "python", 80 | "pygments_lexer": "ipython3", 81 | "version": "3.9.9" 82 | }, 83 | "orig_nbformat": 4, 84 | "vscode": { 85 | "interpreter": { 86 | "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" 87 | } 88 | } 89 | }, 90 | "nbformat": 4, 91 | "nbformat_minor": 2 92 | } 93 | -------------------------------------------------------------------------------- /examples/How_to_count_tokens_with_tiktoken.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# How to count tokens with tiktoken\n", 9 | "\n", 10 | "[`tiktoken`](https://github.com/openai/tiktoken/blob/main/README.md) is a fast open-source tokenizer by OpenAI.\n", 11 | "\n", 12 | "Given a text string (e.g., `\"tiktoken is great!\"`) and an encoding (e.g., `\"gpt2\"`), a tokenizer can split the text string into a list of tokens (e.g., `[\"t\", \"ik\", \"token\", \" is\", \" great\", \"!\"]`).\n", 13 | "\n", 14 | "Splitting text strings into tokens is useful because models like GPT-3 see text in the form of tokens. Knowing how many tokens are in a text string can tell you (a) whether the string is too long for a text model to process and (b) how much an OpenAI API call costs (as usage is priced by token). Different models use different encodings.\n", 15 | "\n", 16 | "`tiktoken` supports three encodings used by OpenAI models:\n", 17 | "\n", 18 | "| Encoding name | OpenAI models |\n", 19 | "|-------------------------|-----------------------------------------------------|\n", 20 | "| `gpt2` (or `r50k_base`) | Most GPT-3 models |\n", 21 | "| `p50k_base` | Code models, `text-davinci-002`, `text-davinci-003` |\n", 22 | "| `cl100k_base` | `text-embedding-ada-002` |\n", 23 | "\n", 24 | "`p50k_base` overlaps substantially with `gpt2`, and for non-code applications, they will usually give the same tokens.\n", 25 | "\n", 26 | "## Tokenizer libraries and languages\n", 27 | "\n", 28 | "For `gpt2` encodings, tokenizers are available in many languages.\n", 29 | "- Python: [tiktoken](https://github.com/openai/tiktoken/blob/main/README.md) (or alternatively [GPT2TokenizerFast](https://huggingface.co/docs/transformers/model_doc/gpt2#transformers.GPT2TokenizerFast))\n", 30 | "- JavaScript: [gpt-3-encoder](https://www.npmjs.com/package/gpt-3-encoder)\n", 31 | "- .NET / C#: [GPT Tokenizer](https://github.com/dluc/openai-tools)\n", 32 | "- Java: [gpt2-tokenizer-java](https://github.com/hyunwoongko/gpt2-tokenizer-java)\n", 33 | "- PHP: [GPT-3-Encoder-PHP](https://github.com/CodeRevolutionPlugins/GPT-3-Encoder-PHP)\n", 34 | "\n", 35 | "(OpenAI makes no endorsements or guarantees of third-party libraries.)\n", 36 | "\n", 37 | "For `p50k_base` and `cl100k_base` encodings, `tiktoken` is the only tokenizer available as of January 2023.\n", 38 | "- Python: [tiktoken](https://github.com/openai/tiktoken/blob/main/README.md)\n", 39 | "\n", 40 | "## How strings are typically tokenized\n", 41 | "\n", 42 | "In English, tokens commonly range in length from one character to one word (e.g., `\"t\"` or `\" great\"`), though in some languages tokens can be shorter than one character or longer than one word. Spaces are usually grouped with the starts of words (e.g., `\" is\"` instead of `\"is \"` or `\" \"`+`\"is\"`). You can quickly check how a string is tokenized at the [OpenAI Tokenizer](https://beta.openai.com/tokenizer)." 43 | ] 44 | }, 45 | { 46 | "attachments": {}, 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "## 0. Install `tiktoken`\n", 51 | "\n", 52 | "In your terminal, install `tiktoken` with `pip`:\n", 53 | "\n", 54 | "```bash\n", 55 | "pip install tiktoken\n", 56 | "```" 57 | ] 58 | }, 59 | { 60 | "attachments": {}, 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "## 1. Import `tiktoken`" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 1, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "import tiktoken\n" 74 | ] 75 | }, 76 | { 77 | "attachments": {}, 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "## 2. Load an encoding\n", 82 | "\n", 83 | "Use `tiktoken.get_encoding()` to load an encoding by name.\n", 84 | "\n", 85 | "The first time this runs, it will require an internet connection to download. Later runs won't need an internet connection." 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": 2, 91 | "metadata": {}, 92 | "outputs": [], 93 | "source": [ 94 | "encoding = tiktoken.get_encoding(\"gpt2\")\n" 95 | ] 96 | }, 97 | { 98 | "attachments": {}, 99 | "cell_type": "markdown", 100 | "metadata": {}, 101 | "source": [ 102 | "## 3. Turn text into tokens with `encoding.encode()`\n", 103 | "\n" 104 | ] 105 | }, 106 | { 107 | "attachments": {}, 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "The `.encode()` method converts a text string into a list of token integers." 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 3, 117 | "metadata": {}, 118 | "outputs": [ 119 | { 120 | "data": { 121 | "text/plain": [ 122 | "[83, 1134, 30001, 318, 1049, 0]" 123 | ] 124 | }, 125 | "execution_count": 3, 126 | "metadata": {}, 127 | "output_type": "execute_result" 128 | } 129 | ], 130 | "source": [ 131 | "encoding.encode(\"tiktoken is great!\")\n" 132 | ] 133 | }, 134 | { 135 | "attachments": {}, 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "Count tokens by counting the length of the list returned by `.encode()`." 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 4, 145 | "metadata": {}, 146 | "outputs": [], 147 | "source": [ 148 | "def num_tokens_from_string(string: str, encoding_name: str) -> int:\n", 149 | " \"\"\"Returns the number of tokens in a text string.\"\"\"\n", 150 | " encoding = tiktoken.get_encoding(encoding_name)\n", 151 | " num_tokens = len(encoding.encode(string))\n", 152 | " return num_tokens\n" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 5, 158 | "metadata": {}, 159 | "outputs": [ 160 | { 161 | "data": { 162 | "text/plain": [ 163 | "6" 164 | ] 165 | }, 166 | "execution_count": 5, 167 | "metadata": {}, 168 | "output_type": "execute_result" 169 | } 170 | ], 171 | "source": [ 172 | "num_tokens_from_string(\"tiktoken is great!\", \"gpt2\")\n" 173 | ] 174 | }, 175 | { 176 | "attachments": {}, 177 | "cell_type": "markdown", 178 | "metadata": {}, 179 | "source": [ 180 | "## 4. Turn tokens into text with `encoding.decode()`" 181 | ] 182 | }, 183 | { 184 | "attachments": {}, 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "`.decode()` converts a list of token integers to a string." 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 6, 194 | "metadata": {}, 195 | "outputs": [ 196 | { 197 | "data": { 198 | "text/plain": [ 199 | "'tiktoken is great!'" 200 | ] 201 | }, 202 | "execution_count": 6, 203 | "metadata": {}, 204 | "output_type": "execute_result" 205 | } 206 | ], 207 | "source": [ 208 | "encoding.decode([83, 1134, 30001, 318, 1049, 0])\n" 209 | ] 210 | }, 211 | { 212 | "attachments": {}, 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "Warning: although `.decode()` can be applied to single tokens, beware that it can be lossy for tokens that aren't on utf-8 boundaries." 217 | ] 218 | }, 219 | { 220 | "attachments": {}, 221 | "cell_type": "markdown", 222 | "metadata": {}, 223 | "source": [ 224 | "For single tokens, `.decode_single_token_bytes()` safely converts a single integer token to the bytes it represents." 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": 7, 230 | "metadata": {}, 231 | "outputs": [ 232 | { 233 | "data": { 234 | "text/plain": [ 235 | "[b't', b'ik', b'token', b' is', b' great', b'!']" 236 | ] 237 | }, 238 | "execution_count": 7, 239 | "metadata": {}, 240 | "output_type": "execute_result" 241 | } 242 | ], 243 | "source": [ 244 | "[encoding.decode_single_token_bytes(token) for token in [83, 1134, 30001, 318, 1049, 0]]\n" 245 | ] 246 | }, 247 | { 248 | "attachments": {}, 249 | "cell_type": "markdown", 250 | "metadata": {}, 251 | "source": [ 252 | "(The `b` in front of the strings indicates that the strings are byte strings.)" 253 | ] 254 | }, 255 | { 256 | "attachments": {}, 257 | "cell_type": "markdown", 258 | "metadata": {}, 259 | "source": [ 260 | "## 5. Comparing encodings\n", 261 | "\n", 262 | "Different encodings can vary in how they split words, group spaces, and handle non-English characters. Using the methods above, we can compare different encodings on a few example strings." 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": 8, 268 | "metadata": {}, 269 | "outputs": [], 270 | "source": [ 271 | "def compare_encodings(example_string: str) -> None:\n", 272 | " \"\"\"Prints a comparison of three string encodings.\"\"\"\n", 273 | " # print the example string\n", 274 | " print(f'\\nExample string: \"{example_string}\"')\n", 275 | " # for each encoding, print the # of tokens, the token integers, and the token bytes\n", 276 | " for encoding_name in [\"gpt2\", \"p50k_base\", \"cl100k_base\"]:\n", 277 | " encoding = tiktoken.get_encoding(encoding_name)\n", 278 | " token_integers = encoding.encode(example_string)\n", 279 | " num_tokens = len(token_integers)\n", 280 | " token_bytes = [encoding.decode_single_token_bytes(token) for token in token_integers]\n", 281 | " print()\n", 282 | " print(f\"{encoding_name}: {num_tokens} tokens\")\n", 283 | " print(f\"token integers: {token_integers}\")\n", 284 | " print(f\"token bytes: {token_bytes}\")\n", 285 | " " 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": 9, 291 | "metadata": {}, 292 | "outputs": [ 293 | { 294 | "name": "stdout", 295 | "output_type": "stream", 296 | "text": [ 297 | "\n", 298 | "Example string: \"antidisestablishmentarianism\"\n", 299 | "\n", 300 | "gpt2: 5 tokens\n", 301 | "token integers: [415, 29207, 44390, 3699, 1042]\n", 302 | "token bytes: [b'ant', b'idis', b'establishment', b'arian', b'ism']\n", 303 | "\n", 304 | "p50k_base: 5 tokens\n", 305 | "token integers: [415, 29207, 44390, 3699, 1042]\n", 306 | "token bytes: [b'ant', b'idis', b'establishment', b'arian', b'ism']\n", 307 | "\n", 308 | "cl100k_base: 6 tokens\n", 309 | "token integers: [519, 85342, 34500, 479, 8997, 2191]\n", 310 | "token bytes: [b'ant', b'idis', b'establish', b'ment', b'arian', b'ism']\n" 311 | ] 312 | } 313 | ], 314 | "source": [ 315 | "compare_encodings(\"antidisestablishmentarianism\")\n" 316 | ] 317 | }, 318 | { 319 | "cell_type": "code", 320 | "execution_count": 10, 321 | "metadata": {}, 322 | "outputs": [ 323 | { 324 | "name": "stdout", 325 | "output_type": "stream", 326 | "text": [ 327 | "\n", 328 | "Example string: \"2 + 2 = 4\"\n", 329 | "\n", 330 | "gpt2: 5 tokens\n", 331 | "token integers: [17, 1343, 362, 796, 604]\n", 332 | "token bytes: [b'2', b' +', b' 2', b' =', b' 4']\n", 333 | "\n", 334 | "p50k_base: 5 tokens\n", 335 | "token integers: [17, 1343, 362, 796, 604]\n", 336 | "token bytes: [b'2', b' +', b' 2', b' =', b' 4']\n", 337 | "\n", 338 | "cl100k_base: 7 tokens\n", 339 | "token integers: [17, 489, 220, 17, 284, 220, 19]\n", 340 | "token bytes: [b'2', b' +', b' ', b'2', b' =', b' ', b'4']\n" 341 | ] 342 | } 343 | ], 344 | "source": [ 345 | "compare_encodings(\"2 + 2 = 4\")\n" 346 | ] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "execution_count": 11, 351 | "metadata": {}, 352 | "outputs": [ 353 | { 354 | "name": "stdout", 355 | "output_type": "stream", 356 | "text": [ 357 | "\n", 358 | "Example string: \"お誕生日おめでとう\"\n", 359 | "\n", 360 | "gpt2: 14 tokens\n", 361 | "token integers: [2515, 232, 45739, 243, 37955, 33768, 98, 2515, 232, 1792, 223, 30640, 30201, 29557]\n", 362 | "token bytes: [b'\\xe3\\x81', b'\\x8a', b'\\xe8\\xaa', b'\\x95', b'\\xe7\\x94\\x9f', b'\\xe6\\x97', b'\\xa5', b'\\xe3\\x81', b'\\x8a', b'\\xe3\\x82', b'\\x81', b'\\xe3\\x81\\xa7', b'\\xe3\\x81\\xa8', b'\\xe3\\x81\\x86']\n", 363 | "\n", 364 | "p50k_base: 14 tokens\n", 365 | "token integers: [2515, 232, 45739, 243, 37955, 33768, 98, 2515, 232, 1792, 223, 30640, 30201, 29557]\n", 366 | "token bytes: [b'\\xe3\\x81', b'\\x8a', b'\\xe8\\xaa', b'\\x95', b'\\xe7\\x94\\x9f', b'\\xe6\\x97', b'\\xa5', b'\\xe3\\x81', b'\\x8a', b'\\xe3\\x82', b'\\x81', b'\\xe3\\x81\\xa7', b'\\xe3\\x81\\xa8', b'\\xe3\\x81\\x86']\n", 367 | "\n", 368 | "cl100k_base: 9 tokens\n", 369 | "token integers: [33334, 45918, 243, 21990, 9080, 33334, 62004, 16556, 78699]\n", 370 | "token bytes: [b'\\xe3\\x81\\x8a', b'\\xe8\\xaa', b'\\x95', b'\\xe7\\x94\\x9f', b'\\xe6\\x97\\xa5', b'\\xe3\\x81\\x8a', b'\\xe3\\x82\\x81', b'\\xe3\\x81\\xa7', b'\\xe3\\x81\\xa8\\xe3\\x81\\x86']\n" 371 | ] 372 | } 373 | ], 374 | "source": [ 375 | "compare_encodings(\"お誕生日おめでとう\")\n" 376 | ] 377 | } 378 | ], 379 | "metadata": { 380 | "kernelspec": { 381 | "display_name": "openai", 382 | "language": "python", 383 | "name": "python3" 384 | }, 385 | "language_info": { 386 | "codemirror_mode": { 387 | "name": "ipython", 388 | "version": 3 389 | }, 390 | "file_extension": ".py", 391 | "mimetype": "text/x-python", 392 | "name": "python", 393 | "nbconvert_exporter": "python", 394 | "pygments_lexer": "ipython3", 395 | "version": "3.9.9" 396 | }, 397 | "orig_nbformat": 4, 398 | "vscode": { 399 | "interpreter": { 400 | "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" 401 | } 402 | } 403 | }, 404 | "nbformat": 4, 405 | "nbformat_minor": 2 406 | } 407 | -------------------------------------------------------------------------------- /examples/How_to_handle_rate_limits.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# How to handle rate limits\n", 8 | "\n", 9 | "When you call the OpenAI API repeatedly, you may encounter error messages that say `429: 'Too Many Requests'` or `RateLimitError`. These error messages come from exceeding the API's rate limits.\n", 10 | "\n", 11 | "Rate limits are a common practice for APIs, and they're put in place for a few different reasons.\n", 12 | "\n", 13 | "- First, they help protect against abuse or misuse of the API. For example, a malicious actor could flood the API with requests in an attempt to overload it or cause disruptions in service. By setting rate limits, OpenAI can prevent this kind of activity.\n", 14 | "- Second, rate limits help ensure that everyone has fair access to the API. If one person or organization makes an excessive number of requests, it could bog down the API for everyone else. By throttling the number of requests that a single user can make, OpenAI ensures that everyone has an opportunity to use the API without experiencing slowdowns.\n", 15 | "- Lastly, rate limits can help OpenAI manage the aggregate load on its infrastructure. If requests to the API increase dramatically, it could tax the servers and cause performance issues. By setting rate limits, OpenAI can help maintain a smooth and consistent experience for all users.\n", 16 | "\n", 17 | "Although hitting rate limits can be frustrating, rate limits exist to protect the reliable operation of the API for its users.\n", 18 | "\n", 19 | "In this guide, we'll share some tips for avoiding and handling rate limit errors." 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "## Default rate limits\n", 27 | "\n", 28 | "As of Sep 2022, the default rate limits are:\n", 29 | "\n", 30 | "\n", 31 | "\n", 32 | " \n", 33 | " \n", 34 | " \n", 35 | " \n", 36 | " \n", 37 | "\n", 38 | "\n", 39 | " \n", 40 | " \n", 41 | " \n", 47 | " \n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 62 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 77 | " \n", 83 | " \n", 84 | "\n", 85 | "
Text Completion & Embedding endpointsCode & Edit endpoints
Free trial users\n", 42 | "
    \n", 43 | "
  • 20 requests / minute
  • \n", 44 | "
  • 150,000 tokens / minute
  • \n", 45 | "
\n", 46 | "
\n", 48 | "
    \n", 49 | "
  • 20 requests / minute
  • \n", 50 | "
  • 150,000 tokens / minute
  • \n", 51 | "
\n", 52 | "
Pay-as-you-go users (in your first 48 hours)\n", 57 | "
    \n", 58 | "
  • 60 requests / minute
  • \n", 59 | "
  • 250,000 davinci tokens / minute (and proportionally more for smaller models)
  • \n", 60 | "
\n", 61 | "
\n", 63 | "
    \n", 64 | "
  • 20 requests / minute
  • \n", 65 | "
  • 150,000 tokens / minute
  • \n", 66 | "
\n", 67 | "
Pay-as-you-go users (after your first 48 hours)\n", 72 | "
    \n", 73 | "
  • 3,000 requests / minute
  • \n", 74 | "
  • 250,000 davinci tokens / minute (and proportionally more for smaller models)
  • \n", 75 | "
\n", 76 | "
\n", 78 | "
    \n", 79 | "
  • 20 requests / minute
  • \n", 80 | "
  • 150,000 tokens / minute
  • \n", 81 | "
\n", 82 | "
\n", 86 | "\n", 87 | "For reference, 1,000 tokens is roughly a page of text.\n", 88 | "\n", 89 | "### Other rate limit resources\n", 90 | "\n", 91 | "Read more about OpenAI's rate limits in the [OpenAI Help Center](https://help.openai.com/en/):\n", 92 | "\n", 93 | "- [Is API usage subject to any rate limits?](https://help.openai.com/en/articles/5955598-is-api-usage-subject-to-any-rate-limits)\n", 94 | "- [How can I solve 429: 'Too Many Requests' errors?](https://help.openai.com/en/articles/5955604-how-can-i-solve-429-too-many-requests-errors)\n", 95 | "\n", 96 | "### Requesting a rate limit increase\n", 97 | "\n", 98 | "If you'd like your organization's rate limit increased, please fill out the following form:\n", 99 | "\n", 100 | "- [OpenAI Rate Limit Increase Request form](https://forms.gle/56ZrwXXoxAN1yt6i9)\n" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "## Example rate limit error\n", 108 | "\n", 109 | "A rate limit error will occur when API requests are sent too quickly. If using the OpenAI Python library, they will look something like:\n", 110 | "\n", 111 | "```\n", 112 | "RateLimitError: Rate limit reached for default-codex in organization org-{id} on requests per min. Limit: 20.000000 / min. Current: 24.000000 / min. Contact support@openai.com if you continue to have issues or if you’d like to request an increase.\n", 113 | "```\n", 114 | "\n", 115 | "Below is example code for triggering a rate limit error." 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "import openai # for making OpenAI API requests\n", 125 | "\n", 126 | "# request a bunch of completions in a loop\n", 127 | "for _ in range(100):\n", 128 | " openai.Completion.create(\n", 129 | " model=\"code-cushman-001\",\n", 130 | " prompt=\"def magic_function():\\n\\t\",\n", 131 | " max_tokens=10,\n", 132 | " )\n" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "## How to avoid rate limit errors\n", 140 | "\n", 141 | "### Retrying with exponential backoff\n", 142 | "\n", 143 | "One easy way to avoid rate limit errors is to automatically retry requests with a random exponential backoff. Retrying with exponential backoff means performing a short sleep when a rate limit error is hit, then retrying the unsuccessful request. If the request is still unsuccessful, the sleep length is increased and the process is repeated. This continues until the request is successful or until a maximum number of retries is reached.\n", 144 | "\n", 145 | "This approach has many benefits:\n", 146 | "\n", 147 | "- Automatic retries means you can recover from rate limit errors without crashes or missing data\n", 148 | "- Exponential backoff means that your first retries can be tried quickly, while still benefiting from longer delays if your first few retries fail\n", 149 | "- Adding random jitter to the delay helps retries from all hitting at the same time\n", 150 | "\n", 151 | "Note that unsuccessful requests contribute to your per-minute limit, so continuously resending a request won’t work.\n", 152 | "\n", 153 | "Below are a few example solutions." 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": {}, 159 | "source": [ 160 | "#### Example #1: Using the Tenacity library\n", 161 | "\n", 162 | "[Tenacity](https://tenacity.readthedocs.io/en/latest/) is an Apache 2.0 licensed general-purpose retrying library, written in Python, to simplify the task of adding retry behavior to just about anything.\n", 163 | "\n", 164 | "To add exponential backoff to your requests, you can use the `tenacity.retry` [decorator](https://peps.python.org/pep-0318/). The following example uses the `tenacity.wait_random_exponential` function to add random exponential backoff to a request.\n", 165 | "\n", 166 | "Note that the Tenacity library is a third-party tool, and OpenAI makes no guarantees about its reliability or security." 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 1, 172 | "metadata": {}, 173 | "outputs": [ 174 | { 175 | "data": { 176 | "text/plain": [ 177 | " JSON: {\n", 178 | " \"choices\": [\n", 179 | " {\n", 180 | " \"finish_reason\": \"length\",\n", 181 | " \"index\": 0,\n", 182 | " \"logprobs\": null,\n", 183 | " \"text\": \" a little girl dreamed of becoming a model.\\n\\nNowadays, that dream\"\n", 184 | " }\n", 185 | " ],\n", 186 | " \"created\": 1662793900,\n", 187 | " \"id\": \"cmpl-5oowO391reUW8RGVfFyzBM1uBs4A5\",\n", 188 | " \"model\": \"text-davinci-002\",\n", 189 | " \"object\": \"text_completion\",\n", 190 | " \"usage\": {\n", 191 | " \"completion_tokens\": 16,\n", 192 | " \"prompt_tokens\": 5,\n", 193 | " \"total_tokens\": 21\n", 194 | " }\n", 195 | "}" 196 | ] 197 | }, 198 | "execution_count": 1, 199 | "metadata": {}, 200 | "output_type": "execute_result" 201 | } 202 | ], 203 | "source": [ 204 | "import openai # for OpenAI API calls\n", 205 | "from tenacity import (\n", 206 | " retry,\n", 207 | " stop_after_attempt,\n", 208 | " wait_random_exponential,\n", 209 | ") # for exponential backoff\n", 210 | "\n", 211 | "\n", 212 | "@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6))\n", 213 | "def completion_with_backoff(**kwargs):\n", 214 | " return openai.Completion.create(**kwargs)\n", 215 | "\n", 216 | "\n", 217 | "completion_with_backoff(model=\"text-davinci-002\", prompt=\"Once upon a time,\")\n" 218 | ] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "metadata": {}, 223 | "source": [ 224 | "#### Example #2: Using the backoff library\n", 225 | "\n", 226 | "Another library that provides function decorators for backoff and retry is [backoff](https://pypi.org/project/backoff/).\n", 227 | "\n", 228 | "Like Tenacity, the backoff library is a third-party tool, and OpenAI makes no guarantees about its reliability or security." 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": 2, 234 | "metadata": {}, 235 | "outputs": [ 236 | { 237 | "data": { 238 | "text/plain": [ 239 | " JSON: {\n", 240 | " \"choices\": [\n", 241 | " {\n", 242 | " \"finish_reason\": \"length\",\n", 243 | " \"index\": 0,\n", 244 | " \"logprobs\": null,\n", 245 | " \"text\": \" two children lived in a poor country village. In the winter, the temperature would\"\n", 246 | " }\n", 247 | " ],\n", 248 | " \"created\": 1662793901,\n", 249 | " \"id\": \"cmpl-5oowPhIdUvshEsF1rBhhwE9KFfI3M\",\n", 250 | " \"model\": \"text-davinci-002\",\n", 251 | " \"object\": \"text_completion\",\n", 252 | " \"usage\": {\n", 253 | " \"completion_tokens\": 16,\n", 254 | " \"prompt_tokens\": 5,\n", 255 | " \"total_tokens\": 21\n", 256 | " }\n", 257 | "}" 258 | ] 259 | }, 260 | "execution_count": 2, 261 | "metadata": {}, 262 | "output_type": "execute_result" 263 | } 264 | ], 265 | "source": [ 266 | "import backoff # for exponential backoff\n", 267 | "import openai # for OpenAI API calls\n", 268 | "\n", 269 | "\n", 270 | "@backoff.on_exception(backoff.expo, openai.error.RateLimitError)\n", 271 | "def completions_with_backoff(**kwargs):\n", 272 | " return openai.Completion.create(**kwargs)\n", 273 | "\n", 274 | "\n", 275 | "completions_with_backoff(model=\"text-davinci-002\", prompt=\"Once upon a time,\")\n" 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "metadata": {}, 281 | "source": [ 282 | "#### Example 3: Manual backoff implementation\n", 283 | "\n", 284 | "If you don't want to use third-party libraries, you can implement your own backoff logic." 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": 3, 290 | "metadata": {}, 291 | "outputs": [ 292 | { 293 | "data": { 294 | "text/plain": [ 295 | " JSON: {\n", 296 | " \"choices\": [\n", 297 | " {\n", 298 | " \"finish_reason\": \"length\",\n", 299 | " \"index\": 0,\n", 300 | " \"logprobs\": null,\n", 301 | " \"text\": \" a man decided to greatly improve his karma by turning his life around.\\n\\n\"\n", 302 | " }\n", 303 | " ],\n", 304 | " \"created\": 1662793903,\n", 305 | " \"id\": \"cmpl-5oowRsCXv3AkUgVJyyo3TQrVq7hIT\",\n", 306 | " \"model\": \"text-davinci-002\",\n", 307 | " \"object\": \"text_completion\",\n", 308 | " \"usage\": {\n", 309 | " \"completion_tokens\": 16,\n", 310 | " \"prompt_tokens\": 5,\n", 311 | " \"total_tokens\": 21\n", 312 | " }\n", 313 | "}" 314 | ] 315 | }, 316 | "execution_count": 3, 317 | "metadata": {}, 318 | "output_type": "execute_result" 319 | } 320 | ], 321 | "source": [ 322 | "# imports\n", 323 | "import random\n", 324 | "import time\n", 325 | "\n", 326 | "import openai\n", 327 | "\n", 328 | "# define a retry decorator\n", 329 | "def retry_with_exponential_backoff(\n", 330 | " func,\n", 331 | " initial_delay: float = 1,\n", 332 | " exponential_base: float = 2,\n", 333 | " jitter: bool = True,\n", 334 | " max_retries: int = 10,\n", 335 | " errors: tuple = (openai.error.RateLimitError,),\n", 336 | "):\n", 337 | " \"\"\"Retry a function with exponential backoff.\"\"\"\n", 338 | "\n", 339 | " def wrapper(*args, **kwargs):\n", 340 | " # Initialize variables\n", 341 | " num_retries = 0\n", 342 | " delay = initial_delay\n", 343 | "\n", 344 | " # Loop until a successful response or max_retries is hit or an exception is raised\n", 345 | " while True:\n", 346 | " try:\n", 347 | " return func(*args, **kwargs)\n", 348 | "\n", 349 | " # Retry on specified errors\n", 350 | " except errors as e:\n", 351 | " # Increment retries\n", 352 | " num_retries += 1\n", 353 | "\n", 354 | " # Check if max retries has been reached\n", 355 | " if num_retries > max_retries:\n", 356 | " raise Exception(\n", 357 | " f\"Maximum number of retries ({max_retries}) exceeded.\"\n", 358 | " )\n", 359 | "\n", 360 | " # Increment the delay\n", 361 | " delay *= exponential_base * (1 + jitter * random.random())\n", 362 | "\n", 363 | " # Sleep for the delay\n", 364 | " time.sleep(delay)\n", 365 | "\n", 366 | " # Raise exceptions for any errors not specified\n", 367 | " except Exception as e:\n", 368 | " raise e\n", 369 | "\n", 370 | " return wrapper\n", 371 | "\n", 372 | "\n", 373 | "@retry_with_exponential_backoff\n", 374 | "def completions_with_backoff(**kwargs):\n", 375 | " return openai.Completion.create(**kwargs)\n", 376 | "\n", 377 | "\n", 378 | "completions_with_backoff(model=\"text-davinci-002\", prompt=\"Once upon a time,\")\n" 379 | ] 380 | }, 381 | { 382 | "cell_type": "markdown", 383 | "metadata": {}, 384 | "source": [ 385 | "## How to maximize throughput of batch processing given rate limits\n", 386 | "\n", 387 | "If you're processing real-time requests from users, backoff and retry is a great strategy to minimize latency while avoiding rate limit errors.\n", 388 | "\n", 389 | "However, if you're processing large volumes of batch data, where throughput matters more than latency, there are a few other things you can do in addition to backoff and retry.\n", 390 | "\n", 391 | "### Proactively adding delay between requests\n", 392 | "\n", 393 | "If you are constantly hitting the rate limit, then backing off, then hitting the rate limit again, then backing off again, it's possible that a good fraction of your request budget will be 'wasted' on requests that need to be retried. This limits your processing throughput, given a fixed rate limit.\n", 394 | "\n", 395 | "Here, one potential solution is to calculate your rate limit and add a delay equal to its reciprocal (e.g., if your rate limit 20 requests per minute, add a delay of 3 seconds to each request). This can help you operate near the rate limit ceiling without hitting it and incurring wasted requests.\n", 396 | "\n", 397 | "#### Example of adding delay to a request" 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": 4, 403 | "metadata": {}, 404 | "outputs": [ 405 | { 406 | "data": { 407 | "text/plain": [ 408 | " JSON: {\n", 409 | " \"choices\": [\n", 410 | " {\n", 411 | " \"finish_reason\": \"length\",\n", 412 | " \"index\": 0,\n", 413 | " \"logprobs\": null,\n", 414 | " \"text\": \" there was an idyllic little farm that sat by a babbling brook\"\n", 415 | " }\n", 416 | " ],\n", 417 | " \"created\": 1662793907,\n", 418 | " \"id\": \"cmpl-5oowVVZnAzdCPtUJ0rifeamtLcZRp\",\n", 419 | " \"model\": \"text-davinci-002\",\n", 420 | " \"object\": \"text_completion\",\n", 421 | " \"usage\": {\n", 422 | " \"completion_tokens\": 16,\n", 423 | " \"prompt_tokens\": 5,\n", 424 | " \"total_tokens\": 21\n", 425 | " }\n", 426 | "}" 427 | ] 428 | }, 429 | "execution_count": 4, 430 | "metadata": {}, 431 | "output_type": "execute_result" 432 | } 433 | ], 434 | "source": [ 435 | "# imports\n", 436 | "import time\n", 437 | "import openai\n", 438 | "\n", 439 | "# Define a function that adds a delay to a Completion API call\n", 440 | "def delayed_completion(delay_in_seconds: float = 1, **kwargs):\n", 441 | " \"\"\"Delay a completion by a specified amount of time.\"\"\"\n", 442 | "\n", 443 | " # Sleep for the delay\n", 444 | " time.sleep(delay_in_seconds)\n", 445 | "\n", 446 | " # Call the Completion API and return the result\n", 447 | " return openai.Completion.create(**kwargs)\n", 448 | "\n", 449 | "\n", 450 | "# Calculate the delay based on your rate limit\n", 451 | "rate_limit_per_minute = 20\n", 452 | "delay = 60.0 / rate_limit_per_minute\n", 453 | "\n", 454 | "delayed_completion(\n", 455 | " delay_in_seconds=delay,\n", 456 | " model=\"text-davinci-002\",\n", 457 | " prompt=\"Once upon a time,\"\n", 458 | ")\n" 459 | ] 460 | }, 461 | { 462 | "cell_type": "markdown", 463 | "metadata": {}, 464 | "source": [ 465 | "\n", 466 | "\n", 467 | "### Batching requests\n", 468 | "\n", 469 | "The OpenAI API has separate limits for requests per minute and tokens per minute.\n", 470 | "\n", 471 | "If you're hitting the limit on requests per minute, but have headroom on tokens per minute, you can increase your throughput by batching multiple tasks into each request. This will allow you to process more tokens per minute, especially with the smaller models.\n", 472 | "\n", 473 | "Sending in a batch of prompts works exactly the same as a normal API call, except that pass in a list of strings to `prompt` parameter instead of a single string.\n", 474 | "\n", 475 | "**Warning:** the response object may not return completions in the order of the prompts, so always remember to match responses back to prompts using the `index` field.\n", 476 | "\n", 477 | "#### Example without batching" 478 | ] 479 | }, 480 | { 481 | "cell_type": "code", 482 | "execution_count": 5, 483 | "metadata": {}, 484 | "outputs": [ 485 | { 486 | "name": "stdout", 487 | "output_type": "stream", 488 | "text": [ 489 | "Once upon a time, before there were grandiloquent tales of the massacre at Fort Mims, there were stories of\n", 490 | "Once upon a time, a full-sized search and rescue was created. However, CIDIs are the addition of requiring\n", 491 | "Once upon a time, Schubert was hot with the films. “Schubert sings of honey, flowers,\n", 492 | "Once upon a time, you could watch these films on your VCR, sometimes years after their initial theatrical release, and there\n", 493 | "Once upon a time, there was a forest. In that forest, the forest animals ruled. The forest animals had their homes\n", 494 | "Once upon a time, there were two programs that complained about false positive scans. Peacock and Midnight Manager alike, only\n", 495 | "Once upon a time, a long, long time ago, tragedy struck. it was the darkest of nights, and there was\n", 496 | "Once upon a time, when Adam was a perfect little gentleman, he was presented at Court as a guarantee of good character.\n", 497 | "Once upon a time, Adam and Eve made a mistake. They ate the fruit from the tree of immortality and split the consequences\n", 498 | "Once upon a time, there was a set of programming fundamental principles known as the \"X model.\" This is a set of\n" 499 | ] 500 | } 501 | ], 502 | "source": [ 503 | "import openai # for making OpenAI API requests\n", 504 | "\n", 505 | "\n", 506 | "num_stories = 10\n", 507 | "prompt = \"Once upon a time,\"\n", 508 | "\n", 509 | "# serial example, with one story completion per request\n", 510 | "for _ in range(num_stories):\n", 511 | " response = openai.Completion.create(\n", 512 | " model=\"curie\",\n", 513 | " prompt=prompt,\n", 514 | " max_tokens=20,\n", 515 | " )\n", 516 | "\n", 517 | " # print story\n", 518 | " print(prompt + response.choices[0].text)\n" 519 | ] 520 | }, 521 | { 522 | "cell_type": "markdown", 523 | "metadata": {}, 524 | "source": [ 525 | "#### Example with batching" 526 | ] 527 | }, 528 | { 529 | "cell_type": "code", 530 | "execution_count": 6, 531 | "metadata": {}, 532 | "outputs": [ 533 | { 534 | "name": "stdout", 535 | "output_type": "stream", 536 | "text": [ 537 | "Once upon a time, there were two sisters, Eliza Pickering and Ariana 'Ari' Lucas. When these lovely\n", 538 | "Once upon a time, Keene was stung by a worm — actually, probably a python — snaking through his leg\n", 539 | "Once upon a time, there was a professor of physics during the depression. It was difficult, during this time, to get\n", 540 | "Once upon a time, before you got sick, you told stories to all and sundry, and your listeners believed in you\n", 541 | "Once upon a time, there was one very old nice donkey. He was incredibly smart, in a very old, kind of\n", 542 | "Once upon a time, the property of a common lodging house was a common cup for all the inhabitants. Betimes a constant\n", 543 | "Once upon a time, in an unspecified country, there was a witch who had an illegal product. It was highly effective,\n", 544 | "Once upon a time, a long time ago, I turned 13, my beautiful dog Duncan swept me up into his jaws like\n", 545 | "Once upon a time, as a thoroughly reformed creature from an army of Nazis, he took On Judgement Day myself and his\n", 546 | "Once upon a time, Capcom made a game for the Atari VCS called Missile Command. While it was innovative at the time\n" 547 | ] 548 | } 549 | ], 550 | "source": [ 551 | "import openai # for making OpenAI API requests\n", 552 | "\n", 553 | "\n", 554 | "num_stories = 10\n", 555 | "prompts = [\"Once upon a time,\"] * num_stories\n", 556 | "\n", 557 | "# batched example, with 10 stories completions per request\n", 558 | "response = openai.Completion.create(\n", 559 | " model=\"curie\",\n", 560 | " prompt=prompts,\n", 561 | " max_tokens=20,\n", 562 | ")\n", 563 | "\n", 564 | "# match completions to prompts by index\n", 565 | "stories = [\"\"] * len(prompts)\n", 566 | "for choice in response.choices:\n", 567 | " stories[choice.index] = prompts[choice.index] + choice.text\n", 568 | "\n", 569 | "# print stories\n", 570 | "for story in stories:\n", 571 | " print(story)\n" 572 | ] 573 | } 574 | ], 575 | "metadata": { 576 | "kernelspec": { 577 | "display_name": "Python 3.9.9 ('openai')", 578 | "language": "python", 579 | "name": "python3" 580 | }, 581 | "language_info": { 582 | "codemirror_mode": { 583 | "name": "ipython", 584 | "version": 3 585 | }, 586 | "file_extension": ".py", 587 | "mimetype": "text/x-python", 588 | "name": "python", 589 | "nbconvert_exporter": "python", 590 | "pygments_lexer": "ipython3", 591 | "version": "3.9.9" 592 | }, 593 | "orig_nbformat": 4, 594 | "vscode": { 595 | "interpreter": { 596 | "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" 597 | } 598 | } 599 | }, 600 | "nbformat": 4, 601 | "nbformat_minor": 2 602 | } 603 | -------------------------------------------------------------------------------- /examples/How_to_stream_completions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# How to stream completions\n", 8 | "\n", 9 | "By default, when you send a prompt to the OpenAI Completions endpoint, it computes the entire completion and sends it back in a single response.\n", 10 | "\n", 11 | "If you're generating very long completions from a davinci-level model, waiting for the response can take many seconds. As of Aug 2022, responses from `text-davinci-002` typically take something like ~1 second plus ~2 seconds per 100 completion tokens.\n", 12 | "\n", 13 | "If you want to get the response faster, you can 'stream' the completion as it's being generated. This allows you to start printing or otherwise processing the beginning of the completion before the entire completion is finished.\n", 14 | "\n", 15 | "To stream completions, set `stream=True` when calling the Completions endpoint. This will return an object that streams back text as [data-only server-sent events](https://app.mode.com/openai/reports/4fce5ba22b5b/runs/f518a0be4495).\n", 16 | "\n", 17 | "Note that using `stream=True` in a production application makes it more difficult to moderate the content of the completions, which has implications for [approved usage](https://beta.openai.com/docs/usage-guidelines).\n", 18 | "\n", 19 | "Below is a Python code example of how to receive streaming completions." 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 1, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "# imports\n", 29 | "import openai # for OpenAI API calls\n", 30 | "import time # for measuring time savings" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "## A typical completion request\n", 38 | "\n", 39 | "With a typical Completions API call, the text is first computed and then returned all at once." 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 2, 45 | "metadata": {}, 46 | "outputs": [ 47 | { 48 | "name": "stdout", 49 | "output_type": "stream", 50 | "text": [ 51 | "Full response received 7.32 seconds after request\n", 52 | "Full text received: 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100\n" 53 | ] 54 | } 55 | ], 56 | "source": [ 57 | "# Example of an OpenAI Completion request\n", 58 | "# https://beta.openai.com/docs/api-reference/completions/create\n", 59 | "\n", 60 | "# record the time before the request is sent\n", 61 | "start_time = time.time()\n", 62 | "\n", 63 | "# send a Completion request to count to 100\n", 64 | "response = openai.Completion.create(\n", 65 | " model='text-davinci-002',\n", 66 | " prompt='1,2,3,',\n", 67 | " max_tokens=193,\n", 68 | " temperature=0,\n", 69 | ")\n", 70 | "\n", 71 | "# calculate the time it took to receive the response\n", 72 | "response_time = time.time() - start_time\n", 73 | "\n", 74 | "# extract the text from the response\n", 75 | "completion_text = response['choices'][0]['text']\n", 76 | "\n", 77 | "# print the time delay and text received\n", 78 | "print(f\"Full response received {response_time:.2f} seconds after request\")\n", 79 | "print(f\"Full text received: {completion_text}\")" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "## A streaming completion request\n", 87 | "\n", 88 | "With a streaming Completions API call, the text is sent back via a series of events. In Python, you can iterate over these events with a `for` loop." 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 3, 94 | "metadata": {}, 95 | "outputs": [ 96 | { 97 | "name": "stdout", 98 | "output_type": "stream", 99 | "text": [ 100 | "Text received: 4 (0.16 seconds after request)\n", 101 | "Text received: , (0.19 seconds after request)\n", 102 | "Text received: 5 (0.21 seconds after request)\n", 103 | "Text received: , (0.24 seconds after request)\n", 104 | "Text received: 6 (0.27 seconds after request)\n", 105 | "Text received: , (0.29 seconds after request)\n", 106 | "Text received: 7 (0.32 seconds after request)\n", 107 | "Text received: , (0.35 seconds after request)\n", 108 | "Text received: 8 (0.37 seconds after request)\n", 109 | "Text received: , (0.40 seconds after request)\n", 110 | "Text received: 9 (0.43 seconds after request)\n", 111 | "Text received: , (0.46 seconds after request)\n", 112 | "Text received: 10 (0.48 seconds after request)\n", 113 | "Text received: , (0.51 seconds after request)\n", 114 | "Text received: 11 (0.54 seconds after request)\n", 115 | "Text received: , (0.56 seconds after request)\n", 116 | "Text received: 12 (0.59 seconds after request)\n", 117 | "Text received: , (0.62 seconds after request)\n", 118 | "Text received: 13 (0.64 seconds after request)\n", 119 | "Text received: , (0.67 seconds after request)\n", 120 | "Text received: 14 (0.70 seconds after request)\n", 121 | "Text received: , (0.72 seconds after request)\n", 122 | "Text received: 15 (0.75 seconds after request)\n", 123 | "Text received: , (0.78 seconds after request)\n", 124 | "Text received: 16 (0.84 seconds after request)\n", 125 | "Text received: , (0.84 seconds after request)\n", 126 | "Text received: 17 (0.86 seconds after request)\n", 127 | "Text received: , (0.89 seconds after request)\n", 128 | "Text received: 18 (0.91 seconds after request)\n", 129 | "Text received: , (0.94 seconds after request)\n", 130 | "Text received: 19 (1.41 seconds after request)\n", 131 | "Text received: , (1.41 seconds after request)\n", 132 | "Text received: 20 (1.41 seconds after request)\n", 133 | "Text received: , (1.41 seconds after request)\n", 134 | "Text received: 21 (1.41 seconds after request)\n", 135 | "Text received: , (1.41 seconds after request)\n", 136 | "Text received: 22 (1.41 seconds after request)\n", 137 | "Text received: , (1.41 seconds after request)\n", 138 | "Text received: 23 (1.41 seconds after request)\n", 139 | "Text received: , (1.41 seconds after request)\n", 140 | "Text received: 24 (1.46 seconds after request)\n", 141 | "Text received: , (1.46 seconds after request)\n", 142 | "Text received: 25 (1.46 seconds after request)\n", 143 | "Text received: , (1.55 seconds after request)\n", 144 | "Text received: 26 (1.61 seconds after request)\n", 145 | "Text received: , (1.65 seconds after request)\n", 146 | "Text received: 27 (1.66 seconds after request)\n", 147 | "Text received: , (1.70 seconds after request)\n", 148 | "Text received: 28 (1.72 seconds after request)\n", 149 | "Text received: , (1.75 seconds after request)\n", 150 | "Text received: 29 (1.78 seconds after request)\n", 151 | "Text received: , (2.05 seconds after request)\n", 152 | "Text received: 30 (2.08 seconds after request)\n", 153 | "Text received: , (2.13 seconds after request)\n", 154 | "Text received: 31 (2.16 seconds after request)\n", 155 | "Text received: , (2.20 seconds after request)\n", 156 | "Text received: 32 (2.26 seconds after request)\n", 157 | "Text received: , (2.28 seconds after request)\n", 158 | "Text received: 33 (2.31 seconds after request)\n", 159 | "Text received: , (2.35 seconds after request)\n", 160 | "Text received: 34 (2.38 seconds after request)\n", 161 | "Text received: , (2.54 seconds after request)\n", 162 | "Text received: 35 (2.55 seconds after request)\n", 163 | "Text received: , (2.59 seconds after request)\n", 164 | "Text received: 36 (2.61 seconds after request)\n", 165 | "Text received: , (2.64 seconds after request)\n", 166 | "Text received: 37 (2.67 seconds after request)\n", 167 | "Text received: , (2.71 seconds after request)\n", 168 | "Text received: 38 (2.86 seconds after request)\n", 169 | "Text received: , (2.89 seconds after request)\n", 170 | "Text received: 39 (2.92 seconds after request)\n", 171 | "Text received: , (2.95 seconds after request)\n", 172 | "Text received: 40 (2.99 seconds after request)\n", 173 | "Text received: , (3.01 seconds after request)\n", 174 | "Text received: 41 (3.04 seconds after request)\n", 175 | "Text received: , (3.08 seconds after request)\n", 176 | "Text received: 42 (3.15 seconds after request)\n", 177 | "Text received: , (3.33 seconds after request)\n", 178 | "Text received: 43 (3.36 seconds after request)\n", 179 | "Text received: , (3.43 seconds after request)\n", 180 | "Text received: 44 (3.47 seconds after request)\n", 181 | "Text received: , (3.50 seconds after request)\n", 182 | "Text received: 45 (3.53 seconds after request)\n", 183 | "Text received: , (3.56 seconds after request)\n", 184 | "Text received: 46 (3.59 seconds after request)\n", 185 | "Text received: , (3.63 seconds after request)\n", 186 | "Text received: 47 (3.65 seconds after request)\n", 187 | "Text received: , (3.68 seconds after request)\n", 188 | "Text received: 48 (3.71 seconds after request)\n", 189 | "Text received: , (3.77 seconds after request)\n", 190 | "Text received: 49 (3.77 seconds after request)\n", 191 | "Text received: , (3.79 seconds after request)\n", 192 | "Text received: 50 (3.82 seconds after request)\n", 193 | "Text received: , (3.85 seconds after request)\n", 194 | "Text received: 51 (3.89 seconds after request)\n", 195 | "Text received: , (3.91 seconds after request)\n", 196 | "Text received: 52 (3.93 seconds after request)\n", 197 | "Text received: , (3.96 seconds after request)\n", 198 | "Text received: 53 (3.98 seconds after request)\n", 199 | "Text received: , (4.04 seconds after request)\n", 200 | "Text received: 54 (4.05 seconds after request)\n", 201 | "Text received: , (4.07 seconds after request)\n", 202 | "Text received: 55 (4.10 seconds after request)\n", 203 | "Text received: , (4.13 seconds after request)\n", 204 | "Text received: 56 (4.19 seconds after request)\n", 205 | "Text received: , (4.20 seconds after request)\n", 206 | "Text received: 57 (4.20 seconds after request)\n", 207 | "Text received: , (4.23 seconds after request)\n", 208 | "Text received: 58 (4.26 seconds after request)\n", 209 | "Text received: , (4.30 seconds after request)\n", 210 | "Text received: 59 (4.31 seconds after request)\n", 211 | "Text received: , (4.59 seconds after request)\n", 212 | "Text received: 60 (4.61 seconds after request)\n", 213 | "Text received: , (4.64 seconds after request)\n", 214 | "Text received: 61 (4.67 seconds after request)\n", 215 | "Text received: , (4.72 seconds after request)\n", 216 | "Text received: 62 (4.73 seconds after request)\n", 217 | "Text received: , (4.76 seconds after request)\n", 218 | "Text received: 63 (4.80 seconds after request)\n", 219 | "Text received: , (4.83 seconds after request)\n", 220 | "Text received: 64 (4.86 seconds after request)\n", 221 | "Text received: , (4.89 seconds after request)\n", 222 | "Text received: 65 (4.92 seconds after request)\n", 223 | "Text received: , (4.94 seconds after request)\n", 224 | "Text received: 66 (4.97 seconds after request)\n", 225 | "Text received: , (5.00 seconds after request)\n", 226 | "Text received: 67 (5.03 seconds after request)\n", 227 | "Text received: , (5.06 seconds after request)\n", 228 | "Text received: 68 (5.09 seconds after request)\n", 229 | "Text received: , (5.14 seconds after request)\n", 230 | "Text received: 69 (5.16 seconds after request)\n", 231 | "Text received: , (5.19 seconds after request)\n", 232 | "Text received: 70 (5.22 seconds after request)\n", 233 | "Text received: , (5.28 seconds after request)\n", 234 | "Text received: 71 (5.30 seconds after request)\n", 235 | "Text received: , (5.33 seconds after request)\n", 236 | "Text received: 72 (5.36 seconds after request)\n", 237 | "Text received: , (5.38 seconds after request)\n", 238 | "Text received: 73 (5.41 seconds after request)\n", 239 | "Text received: , (5.44 seconds after request)\n", 240 | "Text received: 74 (5.48 seconds after request)\n", 241 | "Text received: , (5.51 seconds after request)\n", 242 | "Text received: 75 (5.53 seconds after request)\n", 243 | "Text received: , (5.56 seconds after request)\n", 244 | "Text received: 76 (5.60 seconds after request)\n", 245 | "Text received: , (5.62 seconds after request)\n", 246 | "Text received: 77 (5.65 seconds after request)\n", 247 | "Text received: , (5.68 seconds after request)\n", 248 | "Text received: 78 (5.71 seconds after request)\n", 249 | "Text received: , (5.77 seconds after request)\n", 250 | "Text received: 79 (5.77 seconds after request)\n", 251 | "Text received: , (5.79 seconds after request)\n", 252 | "Text received: 80 (5.82 seconds after request)\n", 253 | "Text received: , (5.85 seconds after request)\n", 254 | "Text received: 81 (5.88 seconds after request)\n", 255 | "Text received: , (5.92 seconds after request)\n", 256 | "Text received: 82 (5.93 seconds after request)\n", 257 | "Text received: , (5.97 seconds after request)\n", 258 | "Text received: 83 (5.98 seconds after request)\n", 259 | "Text received: , (6.01 seconds after request)\n", 260 | "Text received: 84 (6.04 seconds after request)\n", 261 | "Text received: , (6.07 seconds after request)\n", 262 | "Text received: 85 (6.09 seconds after request)\n", 263 | "Text received: , (6.11 seconds after request)\n", 264 | "Text received: 86 (6.14 seconds after request)\n", 265 | "Text received: , (6.17 seconds after request)\n", 266 | "Text received: 87 (6.19 seconds after request)\n", 267 | "Text received: , (6.22 seconds after request)\n", 268 | "Text received: 88 (6.24 seconds after request)\n", 269 | "Text received: , (6.27 seconds after request)\n", 270 | "Text received: 89 (6.30 seconds after request)\n", 271 | "Text received: , (6.31 seconds after request)\n", 272 | "Text received: 90 (6.35 seconds after request)\n", 273 | "Text received: , (6.36 seconds after request)\n", 274 | "Text received: 91 (6.40 seconds after request)\n", 275 | "Text received: , (6.44 seconds after request)\n", 276 | "Text received: 92 (6.46 seconds after request)\n", 277 | "Text received: , (6.49 seconds after request)\n", 278 | "Text received: 93 (6.51 seconds after request)\n", 279 | "Text received: , (6.54 seconds after request)\n", 280 | "Text received: 94 (6.56 seconds after request)\n", 281 | "Text received: , (6.59 seconds after request)\n", 282 | "Text received: 95 (6.62 seconds after request)\n", 283 | "Text received: , (6.64 seconds after request)\n", 284 | "Text received: 96 (6.68 seconds after request)\n", 285 | "Text received: , (6.68 seconds after request)\n", 286 | "Text received: 97 (6.70 seconds after request)\n", 287 | "Text received: , (6.73 seconds after request)\n", 288 | "Text received: 98 (6.75 seconds after request)\n", 289 | "Text received: , (6.78 seconds after request)\n", 290 | "Text received: 99 (6.90 seconds after request)\n", 291 | "Text received: , (6.92 seconds after request)\n", 292 | "Text received: 100 (7.25 seconds after request)\n", 293 | "Full response received 7.25 seconds after request\n", 294 | "Full text received: 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100\n" 295 | ] 296 | } 297 | ], 298 | "source": [ 299 | "# Example of an OpenAI Completion request, using the stream=True option\n", 300 | "# https://beta.openai.com/docs/api-reference/completions/create\n", 301 | "\n", 302 | "# record the time before the request is sent\n", 303 | "start_time = time.time()\n", 304 | "\n", 305 | "# send a Completion request to count to 100\n", 306 | "response = openai.Completion.create(\n", 307 | " model='text-davinci-002',\n", 308 | " prompt='1,2,3,',\n", 309 | " max_tokens=193,\n", 310 | " temperature=0,\n", 311 | " stream=True, # this time, we set stream=True\n", 312 | ")\n", 313 | "\n", 314 | "# create variables to collect the stream of events\n", 315 | "collected_events = []\n", 316 | "completion_text = ''\n", 317 | "# iterate through the stream of events\n", 318 | "for event in response:\n", 319 | " event_time = time.time() - start_time # calculate the time delay of the event\n", 320 | " collected_events.append(event) # save the event response\n", 321 | " event_text = event['choices'][0]['text'] # extract the text\n", 322 | " completion_text += event_text # append the text\n", 323 | " print(f\"Text received: {event_text} ({event_time:.2f} seconds after request)\") # print the delay and text\n", 324 | "\n", 325 | "# print the time delay and text received\n", 326 | "print(f\"Full response received {event_time:.2f} seconds after request\")\n", 327 | "print(f\"Full text received: {completion_text}\")" 328 | ] 329 | }, 330 | { 331 | "cell_type": "markdown", 332 | "metadata": {}, 333 | "source": [ 334 | "## Time comparison\n", 335 | "\n", 336 | "In the example above, both requests took about 7 seconds to fully complete.\n", 337 | "\n", 338 | "However, with the streaming request, you would have received the first token after 0.16 seconds, and subsequent tokens after about ~0.035 seconds each." 339 | ] 340 | } 341 | ], 342 | "metadata": { 343 | "kernelspec": { 344 | "display_name": "Python 3.9.9 ('openai')", 345 | "language": "python", 346 | "name": "python3" 347 | }, 348 | "language_info": { 349 | "codemirror_mode": { 350 | "name": "ipython", 351 | "version": 3 352 | }, 353 | "file_extension": ".py", 354 | "mimetype": "text/x-python", 355 | "name": "python", 356 | "nbconvert_exporter": "python", 357 | "pygments_lexer": "ipython3", 358 | "version": "3.9.9" 359 | }, 360 | "orig_nbformat": 4, 361 | "vscode": { 362 | "interpreter": { 363 | "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" 364 | } 365 | } 366 | }, 367 | "nbformat": 4, 368 | "nbformat_minor": 2 369 | } 370 | -------------------------------------------------------------------------------- /examples/Obtain_dataset.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## 1. Load the dataset\n", 8 | "\n", 9 | "The dataset used in this example is [fine-food reviews](https://www.kaggle.com/snap/amazon-fine-food-reviews) from Amazon. The dataset contains a total of 568,454 food reviews Amazon users left up to October 2012. We will use a subset of this dataset, consisting of 1,000 most recent reviews for illustration purposes. The reviews are in English and tend to be positive or negative. Each review has a ProductId, UserId, Score, review title (Summary) and review body (Text).\n", 10 | "\n", 11 | "We will combine the review summary and review text into a single combined text. The model will encode this combined text and it will output a single vector embedding." 12 | ] 13 | }, 14 | { 15 | "attachments": {}, 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "To run this notebook, you will need to install: pandas, openai, transformers, plotly, matplotlib, scikit-learn, torch (transformer dep), torchvision, and scipy." 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 6, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "# imports\n", 29 | "import pandas as pd\n", 30 | "import tiktoken\n", 31 | "\n", 32 | "from openai.embeddings_utils import get_embedding\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 7, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "# embedding model parameters\n", 42 | "embedding_model = \"text-embedding-ada-002\"\n", 43 | "embedding_encoding = \"cl100k_base\" # this the encoding for text-embedding-ada-002\n", 44 | "max_tokens = 8000 # the maximum for text-embedding-ada-002 is 8191\n" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 8, 50 | "metadata": {}, 51 | "outputs": [ 52 | { 53 | "data": { 54 | "text/html": [ 55 | "
\n", 56 | "\n", 69 | "\n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | "
TimeProductIdUserIdScoreSummaryTextcombined
01351123200B003XPF9BOA3R7JR3FMEBXQB5where does one start...and stop... with a tre...Wanted to save some to bring to my Chicago fam...Title: where does one start...and stop... wit...
11351123200B003JK537SA3JBPC3WFUT5ZP1Arrived in piecesNot pleased at all. When I opened the box, mos...Title: Arrived in pieces; Content: Not pleased...
\n", 105 | "
" 106 | ], 107 | "text/plain": [ 108 | " Time ProductId UserId Score \\\n", 109 | "0 1351123200 B003XPF9BO A3R7JR3FMEBXQB 5 \n", 110 | "1 1351123200 B003JK537S A3JBPC3WFUT5ZP 1 \n", 111 | "\n", 112 | " Summary \\\n", 113 | "0 where does one start...and stop... with a tre... \n", 114 | "1 Arrived in pieces \n", 115 | "\n", 116 | " Text \\\n", 117 | "0 Wanted to save some to bring to my Chicago fam... \n", 118 | "1 Not pleased at all. When I opened the box, mos... \n", 119 | "\n", 120 | " combined \n", 121 | "0 Title: where does one start...and stop... wit... \n", 122 | "1 Title: Arrived in pieces; Content: Not pleased... " 123 | ] 124 | }, 125 | "execution_count": 8, 126 | "metadata": {}, 127 | "output_type": "execute_result" 128 | } 129 | ], 130 | "source": [ 131 | "# load & inspect dataset\n", 132 | "input_datapath = \"data/fine_food_reviews_1k.csv\" # to save space, we provide a pre-filtered dataset\n", 133 | "df = pd.read_csv(input_datapath, index_col=0)\n", 134 | "df = df[[\"Time\", \"ProductId\", \"UserId\", \"Score\", \"Summary\", \"Text\"]]\n", 135 | "df = df.dropna()\n", 136 | "df[\"combined\"] = (\n", 137 | " \"Title: \" + df.Summary.str.strip() + \"; Content: \" + df.Text.str.strip()\n", 138 | ")\n", 139 | "df.head(2)\n" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 9, 145 | "metadata": {}, 146 | "outputs": [ 147 | { 148 | "data": { 149 | "text/plain": [ 150 | "1000" 151 | ] 152 | }, 153 | "execution_count": 9, 154 | "metadata": {}, 155 | "output_type": "execute_result" 156 | } 157 | ], 158 | "source": [ 159 | "# subsample to 1k most recent reviews and remove samples that are too long\n", 160 | "top_n = 1000\n", 161 | "df = df.sort_values(\"Time\").tail(top_n * 2) # first cut to first 2k entries, assuming less than half will be filtered out\n", 162 | "df.drop(\"Time\", axis=1, inplace=True)\n", 163 | "\n", 164 | "encoding = tiktoken.get_encoding(embedding_encoding)\n", 165 | "\n", 166 | "# omit reviews that are too long to embed\n", 167 | "df[\"n_tokens\"] = df.combined.apply(lambda x: len(encoding.encode(x)))\n", 168 | "df = df[df.n_tokens <= max_tokens].tail(top_n)\n", 169 | "len(df)\n" 170 | ] 171 | }, 172 | { 173 | "attachments": {}, 174 | "cell_type": "markdown", 175 | "metadata": {}, 176 | "source": [ 177 | "## 2. Get embeddings and save them for future reuse" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": 10, 183 | "metadata": {}, 184 | "outputs": [], 185 | "source": [ 186 | "# Ensure you have your API key set in your environment per the README: https://github.com/openai/openai-python#usage\n", 187 | "\n", 188 | "# This may take a few minutes\n", 189 | "df[\"embedding\"] = df.combined.apply(lambda x: get_embedding(x, engine=embedding_model))\n", 190 | "df.to_csv(\"data/fine_food_reviews_with_embeddings_1k.csv\")\n" 191 | ] 192 | } 193 | ], 194 | "metadata": { 195 | "kernelspec": { 196 | "display_name": "openai", 197 | "language": "python", 198 | "name": "python3" 199 | }, 200 | "language_info": { 201 | "codemirror_mode": { 202 | "name": "ipython", 203 | "version": 3 204 | }, 205 | "file_extension": ".py", 206 | "mimetype": "text/x-python", 207 | "name": "python", 208 | "nbconvert_exporter": "python", 209 | "pygments_lexer": "ipython3", 210 | "version": "3.9.9 (main, Dec 7 2021, 18:04:56) \n[Clang 13.0.0 (clang-1300.0.29.3)]" 211 | }, 212 | "orig_nbformat": 4, 213 | "vscode": { 214 | "interpreter": { 215 | "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" 216 | } 217 | } 218 | }, 219 | "nbformat": 4, 220 | "nbformat_minor": 2 221 | } 222 | -------------------------------------------------------------------------------- /examples/Regression_using_embeddings.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Regression using the embeddings\n", 8 | "\n", 9 | "Regression means predicting a number, rather than one of the categories. We will predict the score based on the embedding of the review's text. We split the dataset into a training and a testing set for all of the following tasks, so we can realistically evaluate performance on unseen data. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb).\n", 10 | "\n", 11 | "We're predicting the score of the review, which is a number between 1 and 5 (1-star being negative and 5-star positive)." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 1, 17 | "metadata": {}, 18 | "outputs": [ 19 | { 20 | "name": "stdout", 21 | "output_type": "stream", 22 | "text": [ 23 | "ada-002 embedding performance on 1k Amazon reviews: mse=0.62, mae=0.53\n" 24 | ] 25 | } 26 | ], 27 | "source": [ 28 | "import pandas as pd\n", 29 | "import numpy as np\n", 30 | "\n", 31 | "from sklearn.ensemble import RandomForestRegressor\n", 32 | "from sklearn.model_selection import train_test_split\n", 33 | "from sklearn.metrics import mean_squared_error, mean_absolute_error\n", 34 | "\n", 35 | "datafile_path = \"data/fine_food_reviews_with_embeddings_1k.csv\"\n", 36 | "\n", 37 | "df = pd.read_csv(datafile_path)\n", 38 | "df[\"embedding\"] = df.embedding.apply(eval).apply(np.array)\n", 39 | "\n", 40 | "X_train, X_test, y_train, y_test = train_test_split(list(df.embedding.values), df.Score, test_size=0.2, random_state=42)\n", 41 | "\n", 42 | "rfr = RandomForestRegressor(n_estimators=100)\n", 43 | "rfr.fit(X_train, y_train)\n", 44 | "preds = rfr.predict(X_test)\n", 45 | "\n", 46 | "mse = mean_squared_error(y_test, preds)\n", 47 | "mae = mean_absolute_error(y_test, preds)\n", 48 | "\n", 49 | "print(f\"ada-002 embedding performance on 1k Amazon reviews: mse={mse:.2f}, mae={mae:.2f}\")\n" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 2, 55 | "metadata": {}, 56 | "outputs": [ 57 | { 58 | "name": "stdout", 59 | "output_type": "stream", 60 | "text": [ 61 | "Dummy mean prediction performance on Amazon reviews: mse=1.73, mae=1.03\n" 62 | ] 63 | } 64 | ], 65 | "source": [ 66 | "bmse = mean_squared_error(y_test, np.repeat(y_test.mean(), len(y_test)))\n", 67 | "bmae = mean_absolute_error(y_test, np.repeat(y_test.mean(), len(y_test)))\n", 68 | "print(\n", 69 | " f\"Dummy mean prediction performance on Amazon reviews: mse={bmse:.2f}, mae={bmae:.2f}\"\n", 70 | ")\n" 71 | ] 72 | }, 73 | { 74 | "attachments": {}, 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "We can see that the embeddings are able to predict the scores with an average error of 0.53 per score prediction. This is roughly equivalent to predicting half of reviews perfectly, and half off by one star." 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "You could also train a classifier to predict the label, or use the embeddings within an existing ML model to encode free text features." 86 | ] 87 | } 88 | ], 89 | "metadata": { 90 | "kernelspec": { 91 | "display_name": "openai", 92 | "language": "python", 93 | "name": "python3" 94 | }, 95 | "language_info": { 96 | "codemirror_mode": { 97 | "name": "ipython", 98 | "version": 3 99 | }, 100 | "file_extension": ".py", 101 | "mimetype": "text/x-python", 102 | "name": "python", 103 | "nbconvert_exporter": "python", 104 | "pygments_lexer": "ipython3", 105 | "version": "3.9.9" 106 | }, 107 | "orig_nbformat": 4, 108 | "vscode": { 109 | "interpreter": { 110 | "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" 111 | } 112 | } 113 | }, 114 | "nbformat": 4, 115 | "nbformat_minor": 2 116 | } 117 | -------------------------------------------------------------------------------- /examples/Semantic_text_search_using_embeddings.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Semantic text search using embeddings\n", 8 | "\n", 9 | "We can search through all our reviews semantically in a very efficient manner and at very low cost, by simply embedding our search query, and then finding the most similar reviews. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb)." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import pandas as pd\n", 19 | "import numpy as np\n", 20 | "\n", 21 | "datafile_path = \"data/fine_food_reviews_with_embeddings_1k.csv\"\n", 22 | "\n", 23 | "df = pd.read_csv(datafile_path)\n", 24 | "df[\"embedding\"] = df.embedding.apply(eval).apply(np.array)\n" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "Remember to use the documents embedding engine for documents (in this case reviews), and query embedding engine for queries. Note that here we just compare the cosine similarity of the embeddings of the query and the documents, and show top_n best matches." 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": 2, 37 | "metadata": {}, 38 | "outputs": [ 39 | { 40 | "name": "stdout", 41 | "output_type": "stream", 42 | "text": [ 43 | "Good Buy: I liked the beans. They were vacuum sealed, plump and moist. Would recommend them for any use. I personally split and stuck them in some vodka to make vanilla extract. Yum!\n", 44 | "\n", 45 | "Jamaican Blue beans: Excellent coffee bean for roasting. Our family just purchased another 5 pounds for more roasting. Plenty of flavor and mild on acidity when roasted to a dark brown bean and befor\n", 46 | "\n", 47 | "Delicious!: I enjoy this white beans seasoning, it gives a rich flavor to the beans I just love it, my mother in law didn't know about this Zatarain's brand and now she is traying different seasoning\n", 48 | "\n" 49 | ] 50 | } 51 | ], 52 | "source": [ 53 | "from openai.embeddings_utils import get_embedding, cosine_similarity\n", 54 | "\n", 55 | "# search through the reviews for a specific product\n", 56 | "def search_reviews(df, product_description, n=3, pprint=True):\n", 57 | " product_embedding = get_embedding(\n", 58 | " product_description,\n", 59 | " engine=\"text-embedding-ada-002\"\n", 60 | " )\n", 61 | " df[\"similarity\"] = df.embedding.apply(lambda x: cosine_similarity(x, product_embedding))\n", 62 | "\n", 63 | " results = (\n", 64 | " df.sort_values(\"similarity\", ascending=False)\n", 65 | " .head(n)\n", 66 | " .combined.str.replace(\"Title: \", \"\")\n", 67 | " .str.replace(\"; Content:\", \": \")\n", 68 | " )\n", 69 | " if pprint:\n", 70 | " for r in results:\n", 71 | " print(r[:200])\n", 72 | " print()\n", 73 | " return results\n", 74 | "\n", 75 | "\n", 76 | "results = search_reviews(df, \"delicious beans\", n=3)\n" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 3, 82 | "metadata": {}, 83 | "outputs": [ 84 | { 85 | "name": "stdout", 86 | "output_type": "stream", 87 | "text": [ 88 | "Tasty and Quick Pasta: Barilla Whole Grain Fusilli with Vegetable Marinara is tasty and has an excellent chunky vegetable marinara. I just wish there was more of it. If you aren't starving or on a \n", 89 | "\n", 90 | "sooo good: tastes so good. Worth the money. My boyfriend hates wheat pasta and LOVES this. cooks fast tastes great.I love this brand and started buying more of their pastas. Bulk is best.\n", 91 | "\n", 92 | "Handy: Love the idea of ready in a minute pasta and for that alone this product gets praise. The pasta is whole grain so that's a big plus and it actually comes out al dente. The vegetable marinara\n", 93 | "\n" 94 | ] 95 | } 96 | ], 97 | "source": [ 98 | "results = search_reviews(df, \"whole wheat pasta\", n=3)" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "We can search through these reviews easily. To speed up computation, we can use a special algorithm, aimed at faster search through embeddings." 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": 4, 111 | "metadata": {}, 112 | "outputs": [ 113 | { 114 | "name": "stdout", 115 | "output_type": "stream", 116 | "text": [ 117 | "great product, poor delivery: The coffee is excellent and I am a repeat buyer. Problem this time was with the UPS delivery. They left the box in front of my garage door in the middle of the drivewa\n", 118 | "\n" 119 | ] 120 | } 121 | ], 122 | "source": [ 123 | "results = search_reviews(df, \"bad delivery\", n=1)" 124 | ] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "metadata": {}, 129 | "source": [ 130 | "As we can see, this can immediately deliver a lot of value. In this example we show being able to quickly find the examples of delivery failures." 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 5, 136 | "metadata": {}, 137 | "outputs": [ 138 | { 139 | "name": "stdout", 140 | "output_type": "stream", 141 | "text": [ 142 | "Extremely dissapointed: Hi,
I am very disappointed with the past shipment I received of the ONE coconut water. 3 of the boxes were leaking and the coconut water was spoiled.

Thanks." 117 | ] 118 | }, 119 | "metadata": { 120 | "needs_background": "light" 121 | }, 122 | "output_type": "display_data" 123 | } 124 | ], 125 | "source": [ 126 | "import matplotlib.pyplot as plt\n", 127 | "import statsmodels.api as sm\n", 128 | "\n", 129 | "\n", 130 | "correlation = X_test[['percentile_cosine_similarity', 'Score']].corr().values[0,1]\n", 131 | "print('Correlation between user & vector similarity percentile metric and review number of stars (score): %.2f%%' % (100*correlation))\n", 132 | "\n", 133 | "# boxplot of cosine similarity for each score\n", 134 | "X_test.boxplot(column='percentile_cosine_similarity', by='Score')\n", 135 | "plt.title('')\n", 136 | "plt.show()\n", 137 | "plt.close()" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "We can observe a weak trend, showing that the higher the similarity score between the user and the product embedding, the higher the review score. Therefore, the user and product embeddings can weakly predict the review score - even before the user receives the product!\n", 145 | "\n", 146 | "Because this signal works in a different way than the more commonly used collaborative filtering, it can act as an additional feature to slightly improve the performance on existing problems." 147 | ] 148 | } 149 | ], 150 | "metadata": { 151 | "interpreter": { 152 | "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" 153 | }, 154 | "kernelspec": { 155 | "display_name": "Python 3.7.3 64-bit ('base': conda)", 156 | "name": "python3" 157 | }, 158 | "language_info": { 159 | "codemirror_mode": { 160 | "name": "ipython", 161 | "version": 3 162 | }, 163 | "file_extension": ".py", 164 | "mimetype": "text/x-python", 165 | "name": "python", 166 | "nbconvert_exporter": "python", 167 | "pygments_lexer": "ipython3", 168 | "version": "3.7.3" 169 | }, 170 | "orig_nbformat": 4 171 | }, 172 | "nbformat": 4, 173 | "nbformat_minor": 2 174 | } 175 | -------------------------------------------------------------------------------- /examples/azure/completions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Azure completions example\n", 8 | "In this example we'll try to go over all operations needed to get completions working using the Azure endpoints. \\\n", 9 | "This example focuses on completions but also touches on some other operations that are also available using the API. This example is meant to be a quick way of showing simple operations and is not meant as a tutorial." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import openai\n", 19 | "from openai import cli" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "## Setup\n", 27 | "For the following sections to work properly we first have to setup some things. Let's start with the `api_base` and `api_version`. To find your `api_base` go to https://portal.azure.com, find your resource and then under \"Resource Management\" -> \"Keys and Endpoints\" look for the \"Endpoint\" value." 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "openai.api_version = '2022-12-01'\n", 37 | "openai.api_base = '' # Please add your endpoint here" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "We next have to setup the `api_type` and `api_key`. We can either get the key from the portal or we can get it through Microsoft Active Directory Authentication. Depending on this the `api_type` is either `azure` or `azure_ad`." 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "### Setup: Portal\n", 52 | "Let's first look at getting the key from the portal. Go to https://portal.azure.com, find your resource and then under \"Resource Management\" -> \"Keys and Endpoints\" look for one of the \"Keys\" values." 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": null, 58 | "metadata": {}, 59 | "outputs": [], 60 | "source": [ 61 | "openai.api_type = 'azure'\n", 62 | "openai.api_key = '' # Please add your api key here" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "### (Optional) Setup: Microsoft Active Directory Authentication\n", 70 | "Let's now see how we can get a key via Microsoft Active Directory Authentication. Uncomment the following code if you want to use Active Directory Authentication instead of keys from the portal." 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [ 79 | "# from azure.identity import DefaultAzureCredential\n", 80 | "\n", 81 | "# default_credential = DefaultAzureCredential()\n", 82 | "# token = default_credential.get_token(\"https://cognitiveservices.azure.com/.default\")\n", 83 | "\n", 84 | "# openai.api_type = 'azure_ad'\n", 85 | "# openai.api_key = token.token" 86 | ] 87 | }, 88 | { 89 | "attachments": {}, 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "## Deployments\n", 94 | "In this section we are going to create a deployment using the `text-davinci-002` model that we can then use to create completions." 95 | ] 96 | }, 97 | { 98 | "attachments": {}, 99 | "cell_type": "markdown", 100 | "metadata": {}, 101 | "source": [ 102 | "### Deployments: Create manually\n", 103 | "Create a new deployment by going to your Resource in your portal under \"Resource Management\" -> \"Model deployments\". Select `text-davinci-002` as the model." 104 | ] 105 | }, 106 | { 107 | "attachments": {}, 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "### (Optional) Deployments: Create programatically\n", 112 | "We can also create a deployment using code:" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "metadata": {}, 119 | "outputs": [], 120 | "source": [ 121 | "model = \"text-davinci-002\"\n", 122 | "\n", 123 | "# Now let's create the deployment\n", 124 | "print(f'Creating a new deployment with model: {model}')\n", 125 | "result = openai.Deployment.create(model=model, scale_settings={\"scale_type\":\"standard\"})\n", 126 | "deployment_id = result[\"id\"]\n", 127 | "print(f'Successfully created deployment with id: {deployment_id}')" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "### (Optional) Deployments: Wait for deployment to succeed\n", 135 | "Now let's check the status of the newly created deployment and wait till it is succeeded." 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [ 144 | "print(f'Checking for deployment status.')\n", 145 | "resp = openai.Deployment.retrieve(id=deployment_id)\n", 146 | "status = resp[\"status\"]\n", 147 | "print(f'Deployment {deployment_id} has status: {status}')\n", 148 | "while status not in [\"succeeded\", \"failed\"]:\n", 149 | " resp = openai.Deployment.retrieve(id=deployment_id)\n", 150 | " status = resp[\"status\"]\n", 151 | " print(f'Deployment {deployment_id} has status: {status}')" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": {}, 157 | "source": [ 158 | "### Completions\n", 159 | "Now let's send a sample completion to the deployment." 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "metadata": {}, 166 | "outputs": [], 167 | "source": [ 168 | "prompt = \"The food was delicious and the waiter\"\n", 169 | "completion = openai.Completion.create(deployment_id=deployment_id,\n", 170 | " prompt=prompt, stop=\".\", temperature=0)\n", 171 | " \n", 172 | "print(f\"{prompt}{completion['choices'][0]['text']}.\")" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "### (Optional) Deployments: Delete\n", 180 | "Finally let's delete the deployment" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": {}, 187 | "outputs": [], 188 | "source": [ 189 | "print(f'Deleting deployment: {deployment_id}')\n", 190 | "openai.Deployment.delete(sid=deployment_id)" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "metadata": {}, 197 | "outputs": [], 198 | "source": [] 199 | } 200 | ], 201 | "metadata": { 202 | "kernelspec": { 203 | "display_name": "Python 3 (ipykernel)", 204 | "language": "python", 205 | "name": "python3" 206 | }, 207 | "language_info": { 208 | "codemirror_mode": { 209 | "name": "ipython", 210 | "version": 3 211 | }, 212 | "file_extension": ".py", 213 | "mimetype": "text/x-python", 214 | "name": "python", 215 | "nbconvert_exporter": "python", 216 | "pygments_lexer": "ipython3", 217 | "version": "3.10.8" 218 | }, 219 | "vscode": { 220 | "interpreter": { 221 | "hash": "3a5103089ab7e7c666b279eeded403fcec76de49a40685dbdfe9f9c78ad97c17" 222 | } 223 | } 224 | }, 225 | "nbformat": 4, 226 | "nbformat_minor": 2 227 | } 228 | -------------------------------------------------------------------------------- /examples/azure/embeddings.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# Azure embeddings example\n", 9 | "In this example we'll try to go over all operations for embeddings that can be done using the Azure endpoints. \\\n", 10 | "This example focuses on embeddings but also touches some other operations that are also available using the API. This example is meant to be a quick way of showing simple operations and is not meant as a tutorial." 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "import openai\n", 20 | "from openai import cli" 21 | ] 22 | }, 23 | { 24 | "attachments": {}, 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "## Setup\n", 29 | "For the following sections to work properly we first have to setup some things. Let's start with the `api_base` and `api_version`. To find your `api_base` go to https://portal.azure.com, find your resource and then under \"Resource Management\" -> \"Keys and Endpoints\" look for the \"Endpoint\" value." 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "openai.api_version = '2022-12-01'\n", 39 | "openai.api_base = '' # Please add your endpoint here" 40 | ] 41 | }, 42 | { 43 | "attachments": {}, 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "We next have to setup the `api_type` and `api_key`. We can either get the key from the portal or we can get it through Microsoft Active Directory Authentication. Depending on this the `api_type` is either `azure` or `azure_ad`." 48 | ] 49 | }, 50 | { 51 | "attachments": {}, 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "### Setup: Portal\n", 56 | "Let's first look at getting the key from the portal. Go to https://portal.azure.com, find your resource and then under \"Resource Management\" -> \"Keys and Endpoints\" look for one of the \"Keys\" values." 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "openai.api_type = 'azure'\n", 66 | "openai.api_key = '' # Please add your api key here" 67 | ] 68 | }, 69 | { 70 | "attachments": {}, 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "### (Optional) Setup: Microsoft Active Directory Authentication\n", 75 | "Let's now see how we can get a key via Microsoft Active Directory Authentication. Uncomment the following code if you want to use Active Directory Authentication instead of keys from the portal." 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "# from azure.identity import DefaultAzureCredential\n", 85 | "\n", 86 | "# default_credential = DefaultAzureCredential()\n", 87 | "# token = default_credential.get_token(\"https://cognitiveservices.azure.com/.default\")\n", 88 | "\n", 89 | "# openai.api_type = 'azure_ad'\n", 90 | "# openai.api_key = token.token" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "## Deployments\n", 98 | "In this section we are going to create a deployment that we can use to create embeddings." 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "### Deployments: Create manually\n", 106 | "Let's create a deployment using the `text-similarity-curie-001` model. Create a new deployment by going to your Resource in your portal under \"Resource Management\" -> \"Model deployments\"." 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "### (Optional) Deployments: Create programatically\n", 114 | "We can also create a deployment using code:" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "model = \"text-similarity-curie-001\"\n", 124 | "\n", 125 | "# Now let's create the deployment\n", 126 | "print(f'Creating a new deployment with model: {model}')\n", 127 | "result = openai.Deployment.create(model=model, scale_settings={\"scale_type\":\"standard\"})\n", 128 | "deployment_id = result[\"id\"]" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "### (Optional) Deployments: Retrieving\n", 136 | "Now let's check the status of the newly created deployment" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "print(f'Checking for deployment status.')\n", 146 | "resp = openai.Deployment.retrieve(id=deployment_id)\n", 147 | "status = resp[\"status\"]\n", 148 | "print(f'Deployment {deployment_id} is with status: {status}')" 149 | ] 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "metadata": {}, 154 | "source": [ 155 | "### Deployments: Listing\n", 156 | "Now because creating a new deployment takes a long time, let's look in the subscription for an already finished deployment that succeeded." 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "print('While deployment running, selecting a completed one that supports embeddings.')\n", 166 | "deployment_id = None\n", 167 | "result = openai.Deployment.list()\n", 168 | "for deployment in result.data:\n", 169 | " if deployment[\"status\"] != \"succeeded\":\n", 170 | " continue\n", 171 | " \n", 172 | " model = openai.Model.retrieve(deployment[\"model\"])\n", 173 | " if model[\"capabilities\"][\"embeddings\"] != True:\n", 174 | " continue\n", 175 | " \n", 176 | " deployment_id = deployment[\"id\"]\n", 177 | " break\n", 178 | "\n", 179 | "if not deployment_id:\n", 180 | " print('No deployment with status: succeeded found.')\n", 181 | "else:\n", 182 | " print(f'Found a succeeded deployment that supports embeddings with id: {deployment_id}.')" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "### Embeddings\n", 190 | "Now let's send a sample embedding to the deployment." 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "metadata": {}, 197 | "outputs": [], 198 | "source": [ 199 | "embeddings = openai.Embedding.create(deployment_id=deployment_id,\n", 200 | " input=\"The food was delicious and the waiter...\")\n", 201 | " \n", 202 | "print(embeddings)" 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "### (Optional) Deployments: Delete\n", 210 | "Finally let's delete the deployment" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": null, 216 | "metadata": {}, 217 | "outputs": [], 218 | "source": [ 219 | "print(f'Deleting deployment: {deployment_id}')\n", 220 | "openai.Deployment.delete(sid=deployment_id)" 221 | ] 222 | } 223 | ], 224 | "metadata": { 225 | "kernelspec": { 226 | "display_name": "Python 3", 227 | "language": "python", 228 | "name": "python3" 229 | }, 230 | "language_info": { 231 | "codemirror_mode": { 232 | "name": "ipython", 233 | "version": 3 234 | }, 235 | "file_extension": ".py", 236 | "mimetype": "text/x-python", 237 | "name": "python", 238 | "nbconvert_exporter": "python", 239 | "pygments_lexer": "ipython3", 240 | "version": "3.10.8" 241 | }, 242 | "vscode": { 243 | "interpreter": { 244 | "hash": "3a5103089ab7e7c666b279eeded403fcec76de49a40685dbdfe9f9c78ad97c17" 245 | } 246 | } 247 | }, 248 | "nbformat": 4, 249 | "nbformat_minor": 2 250 | } 251 | -------------------------------------------------------------------------------- /examples/azure/finetuning.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Azure Fine tuning example\n", 8 | "In this example we'll try to go over all operations that can be done using the Azure endpoints and their differences with the openAi endpoints (if any).
\n", 9 | "This example focuses on finetuning but also touches on the majority of operations that are available using the API. This example is meant to be a quick way of showing simple operations and is not meant as a finetune model adaptation tutorial.\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import openai\n", 19 | "from openai import cli" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "## Setup\n", 27 | "For the following sections to work properly we first have to setup some things. Let's start with the `api_base` and `api_version`. To find your `api_base` go to https://portal.azure.com, find your resource and then under \"Resource Management\" -> \"Keys and Endpoints\" look for the \"Endpoint\" value." 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "openai.api_version = '2022-12-01'\n", 37 | "openai.api_base = '' # Please add your endpoint here" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "We next have to setup the `api_type` and `api_key`. We can either get the key from the portal or we can get it through Microsoft Active Directory Authentication. Depending on this the `api_type` is either `azure` or `azure_ad`." 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "### Setup: Portal\n", 52 | "Let's first look at getting the key from the portal. Go to https://portal.azure.com, find your resource and then under \"Resource Management\" -> \"Keys and Endpoints\" look for one of the \"Keys\" values." 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": null, 58 | "metadata": {}, 59 | "outputs": [], 60 | "source": [ 61 | "openai.api_type = 'azure'\n", 62 | "openai.api_key = '' # Please add your api key here" 63 | ] 64 | }, 65 | { 66 | "attachments": {}, 67 | "cell_type": "markdown", 68 | "metadata": {}, 69 | "source": [ 70 | "### (Optional) Setup: Microsoft Active Directory Authentication\n", 71 | "Let's now see how we can get a key via Microsoft Active Directory Authentication. Uncomment the following code if you want to use Active Directory Authentication instead of keys from the portal." 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": null, 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "# from azure.identity import DefaultAzureCredential\n", 81 | "\n", 82 | "# default_credential = DefaultAzureCredential()\n", 83 | "# token = default_credential.get_token(\"https://cognitiveservices.azure.com/.default\")\n", 84 | "\n", 85 | "# openai.api_type = 'azure_ad'\n", 86 | "# openai.api_key = token.token" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "## Files\n", 94 | "In the next section we will focus on the files operations: importing, listing, retrieving, deleting. For this we need to create 2 temporary files with some sample data. For the sake of simplicity, we will use the same data for training and validation." 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "import shutil\n", 104 | "import json\n", 105 | "\n", 106 | "training_file_name = 'training.jsonl'\n", 107 | "validation_file_name = 'validation.jsonl'\n", 108 | "\n", 109 | "sample_data = [{\"prompt\": \"When I go to the store, I want an\", \"completion\": \"apple.\"},\n", 110 | " {\"prompt\": \"When I go to work, I want a\", \"completion\": \"coffee.\"},\n", 111 | " {\"prompt\": \"When I go home, I want a\", \"completion\": \"soda.\"}]\n", 112 | "\n", 113 | "print(f'Generating the training file: {training_file_name}')\n", 114 | "with open(training_file_name, 'w') as training_file:\n", 115 | " for entry in sample_data:\n", 116 | " json.dump(entry, training_file)\n", 117 | " training_file.write('\\n')\n", 118 | "\n", 119 | "print(f'Copying the training file to the validation file')\n", 120 | "shutil.copy(training_file_name, validation_file_name)" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "### Files: Listing\n", 128 | "List all of the uploaded files and check for the ones that are named \"training.jsonl\" or \"validation.jsonl\"" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": null, 134 | "metadata": {}, 135 | "outputs": [], 136 | "source": [ 137 | "print('Checking for existing uploaded files.')\n", 138 | "results = []\n", 139 | "files = openai.File.list().data\n", 140 | "print(f'Found {len(files)} total uploaded files in the subscription.')\n", 141 | "for item in files:\n", 142 | " if item[\"filename\"] in [training_file_name, validation_file_name]:\n", 143 | " results.append(item[\"id\"])\n", 144 | "print(f'Found {len(results)} already uploaded files that match our names.')\n" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": {}, 150 | "source": [ 151 | "### Files: Deleting\n", 152 | "Let's now delete those found files (if any) since we're going to be re-uploading them next." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "print(f'Deleting already uploaded files...')\n", 162 | "for id in results:\n", 163 | " openai.File.delete(sid = id)\n" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "### Files: Importing & Retrieving\n", 171 | "Now, let's import our two files ('training.jsonl' and 'validation.jsonl') and keep those IDs since we're going to use them later for finetuning.
\n", 172 | "For this operation we are going to use the cli wrapper which does a bit more checks before uploading and also gives us progress. In addition, after uploading we're going to check the status our import until it has succeeded (or failed if something goes wrong)" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [ 181 | "import time\n", 182 | "\n", 183 | "def check_status(training_id, validation_id):\n", 184 | " train_status = openai.File.retrieve(training_id)[\"status\"]\n", 185 | " valid_status = openai.File.retrieve(validation_id)[\"status\"]\n", 186 | " print(f'Status (training_file | validation_file): {train_status} | {valid_status}')\n", 187 | " return (train_status, valid_status)\n", 188 | "\n", 189 | "#importing our two files\n", 190 | "training_id = cli.FineTune._get_or_upload(training_file_name, True)\n", 191 | "validation_id = cli.FineTune._get_or_upload(validation_file_name, True)\n", 192 | "\n", 193 | "#checking the status of the imports\n", 194 | "(train_status, valid_status) = check_status(training_id, validation_id)\n", 195 | "\n", 196 | "while train_status not in [\"succeeded\", \"failed\"] or valid_status not in [\"succeeded\", \"failed\"]:\n", 197 | " time.sleep(1)\n", 198 | " (train_status, valid_status) = check_status(training_id, validation_id)\n" 199 | ] 200 | }, 201 | { 202 | "cell_type": "markdown", 203 | "metadata": {}, 204 | "source": [ 205 | "### Files: Downloading\n", 206 | "Now let's download one of the files, the training file for example, to check that everything was in order during importing and all bits are there." 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": null, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "print(f'Downloading training file: {training_id}')\n", 216 | "result = openai.File.download(training_id)\n", 217 | "print(result.decode('utf-8'))" 218 | ] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "metadata": {}, 223 | "source": [ 224 | "## Finetune\n", 225 | "In this section we are going to use the two training and validation files that we imported in the previous section, to train a finetune model." 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "### Finetune: Adapt\n", 233 | "First let's create the finetune adaptation job." 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": null, 239 | "metadata": {}, 240 | "outputs": [], 241 | "source": [ 242 | "create_args = {\n", 243 | " \"training_file\": training_id,\n", 244 | " \"validation_file\": validation_id,\n", 245 | " \"model\": \"babbage\",\n", 246 | " \"compute_classification_metrics\": True,\n", 247 | " \"classification_n_classes\": 3,\n", 248 | " \"n_epochs\": 20,\n", 249 | " \"batch_size\": 3,\n", 250 | " \"learning_rate_multiplier\": 0.3\n", 251 | "}\n", 252 | "resp = openai.FineTune.create(**create_args)\n", 253 | "job_id = resp[\"id\"]\n", 254 | "status = resp[\"status\"]\n", 255 | "\n", 256 | "print(f'Fine-tunning model with jobID: {job_id}.')" 257 | ] 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "metadata": {}, 262 | "source": [ 263 | "### Finetune: Streaming\n", 264 | "While the job runs, we can subscribe to the streaming events to check the progress of the operation." 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": null, 270 | "metadata": {}, 271 | "outputs": [], 272 | "source": [ 273 | "import signal\n", 274 | "import datetime\n", 275 | "\n", 276 | "def signal_handler(sig, frame):\n", 277 | " status = openai.FineTune.retrieve(job_id).status\n", 278 | " print(f\"Stream interrupted. Job is still {status}.\")\n", 279 | " return\n", 280 | "\n", 281 | "print(f'Streaming events for the fine-tuning job: {job_id}')\n", 282 | "signal.signal(signal.SIGINT, signal_handler)\n", 283 | "\n", 284 | "events = openai.FineTune.stream_events(job_id)\n", 285 | "try:\n", 286 | " for event in events:\n", 287 | " print(f'{datetime.datetime.fromtimestamp(event[\"created_at\"])} {event[\"message\"]}')\n", 288 | "\n", 289 | "except Exception:\n", 290 | " print(\"Stream interrupted (client disconnected).\")" 291 | ] 292 | }, 293 | { 294 | "cell_type": "markdown", 295 | "metadata": {}, 296 | "source": [ 297 | "### Finetune: Listing and Retrieving\n", 298 | "Now let's check that our operation was successful and in addition we can look at all of the finetuning operations using a list operation." 299 | ] 300 | }, 301 | { 302 | "cell_type": "code", 303 | "execution_count": null, 304 | "metadata": {}, 305 | "outputs": [], 306 | "source": [ 307 | "status = openai.FineTune.retrieve(id=job_id)[\"status\"]\n", 308 | "if status not in [\"succeeded\", \"failed\"]:\n", 309 | " print(f'Job not in terminal status: {status}. Waiting.')\n", 310 | " while status not in [\"succeeded\", \"failed\"]:\n", 311 | " time.sleep(2)\n", 312 | " status = openai.FineTune.retrieve(id=job_id)[\"status\"]\n", 313 | " print(f'Status: {status}')\n", 314 | "else:\n", 315 | " print(f'Finetune job {job_id} finished with status: {status}')\n", 316 | "\n", 317 | "print('Checking other finetune jobs in the subscription.')\n", 318 | "result = openai.FineTune.list()\n", 319 | "print(f'Found {len(result.data)} finetune jobs.')" 320 | ] 321 | }, 322 | { 323 | "cell_type": "markdown", 324 | "metadata": {}, 325 | "source": [ 326 | "### Finetune: Deleting\n", 327 | "Finally we can delete our finetune job.
\n", 328 | "WARNING: Please skip this step if you want to continue with the next section as the finetune model is needed. (The delete code is commented out by default)" 329 | ] 330 | }, 331 | { 332 | "cell_type": "code", 333 | "execution_count": null, 334 | "metadata": {}, 335 | "outputs": [], 336 | "source": [ 337 | "# openai.FineTune.delete(sid=job_id)" 338 | ] 339 | }, 340 | { 341 | "cell_type": "markdown", 342 | "metadata": {}, 343 | "source": [ 344 | "## Deployments\n", 345 | "In this section we are going to create a deployment using the finetune model that we just adapted and then used the deployment to create a simple completion operation." 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": {}, 351 | "source": [ 352 | "### Deployments: Create\n", 353 | "Let's create a deployment using the fine-tune model." 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": null, 359 | "metadata": {}, 360 | "outputs": [], 361 | "source": [ 362 | "#Fist let's get the model of the previous job:\n", 363 | "result = openai.FineTune.retrieve(id=job_id)\n", 364 | "if result[\"status\"] == 'succeeded':\n", 365 | " model = result[\"fine_tuned_model\"]\n", 366 | "\n", 367 | "# Now let's create the deployment\n", 368 | "print(f'Creating a new deployment with model: {model}')\n", 369 | "result = openai.Deployment.create(model=model, scale_settings={\"scale_type\":\"standard\"})\n", 370 | "deployment_id = result[\"id\"]\n" 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": {}, 376 | "source": [ 377 | "### Deployments: Retrieving\n", 378 | "Now let's check the status of the newly created deployment" 379 | ] 380 | }, 381 | { 382 | "cell_type": "code", 383 | "execution_count": null, 384 | "metadata": {}, 385 | "outputs": [], 386 | "source": [ 387 | "print(f'Checking for deployment status.')\n", 388 | "resp = openai.Deployment.retrieve(id=deployment_id)\n", 389 | "status = resp[\"status\"]\n", 390 | "print(f'Deployment {deployment_id} is with status: {status}')\n" 391 | ] 392 | }, 393 | { 394 | "cell_type": "markdown", 395 | "metadata": {}, 396 | "source": [ 397 | "### Deployments: Listing\n", 398 | "Now because creating a new deployment takes a long time, let's look in the subscription for an already finished deployment that succeeded." 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": null, 404 | "metadata": {}, 405 | "outputs": [], 406 | "source": [ 407 | "print('While deployment running, selecting a completed one.')\n", 408 | "deployment_id = None\n", 409 | "result = openai.Deployment.list()\n", 410 | "for deployment in result.data:\n", 411 | " if deployment[\"status\"] == \"succeeded\":\n", 412 | " deployment_id = deployment[\"id\"]\n", 413 | " break\n", 414 | "\n", 415 | "if not deployment_id:\n", 416 | " print('No deployment with status: succeeded found.')\n", 417 | "else:\n", 418 | " print(f'Found a successful deployment with id: {deployment_id}.')\n" 419 | ] 420 | }, 421 | { 422 | "cell_type": "markdown", 423 | "metadata": {}, 424 | "source": [ 425 | "### Completions\n", 426 | "Now let's send a sample completion to the deployment." 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": null, 432 | "metadata": {}, 433 | "outputs": [], 434 | "source": [ 435 | "print('Sending a test completion job')\n", 436 | "start_phrase = 'When I go home, I want a'\n", 437 | "response = openai.Completion.create(deployment_id=deployment_id, prompt=start_phrase, temperature=0, stop=\".\")\n", 438 | "text = response['choices'][0]['text'].replace('\\n', '').replace(' .', '.').strip()\n", 439 | "print(f'\"{start_phrase} {text}.\"')" 440 | ] 441 | }, 442 | { 443 | "cell_type": "markdown", 444 | "metadata": {}, 445 | "source": [ 446 | "### Deployments: Delete\n", 447 | "Finally let's delete the deployment" 448 | ] 449 | }, 450 | { 451 | "cell_type": "code", 452 | "execution_count": null, 453 | "metadata": {}, 454 | "outputs": [], 455 | "source": [ 456 | "print(f'Deleting deployment: {deployment_id}')\n", 457 | "openai.Deployment.delete(sid=deployment_id)" 458 | ] 459 | }, 460 | { 461 | "cell_type": "markdown", 462 | "metadata": {}, 463 | "source": [ 464 | "Thank you" 465 | ] 466 | } 467 | ], 468 | "metadata": { 469 | "kernelspec": { 470 | "display_name": "Python 3", 471 | "language": "python", 472 | "name": "python3" 473 | }, 474 | "language_info": { 475 | "codemirror_mode": { 476 | "name": "ipython", 477 | "version": 3 478 | }, 479 | "file_extension": ".py", 480 | "mimetype": "text/x-python", 481 | "name": "python", 482 | "nbconvert_exporter": "python", 483 | "pygments_lexer": "ipython3", 484 | "version": "3.10.8" 485 | }, 486 | "vscode": { 487 | "interpreter": { 488 | "hash": "3a5103089ab7e7c666b279eeded403fcec76de49a40685dbdfe9f9c78ad97c17" 489 | } 490 | } 491 | }, 492 | "nbformat": 4, 493 | "nbformat_minor": 2 494 | } 495 | -------------------------------------------------------------------------------- /examples/data/25000_spend_dataset_current.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/examples/data/25000_spend_dataset_current.csv -------------------------------------------------------------------------------- /examples/data/labelled_transactions.csv: -------------------------------------------------------------------------------- 1 | Date,Supplier,Description,Transaction value (£),Classification 2 | 15/08/2016,Creative Video Productions Ltd,Kelvin Hall,26866,Other 3 | 29/05/2017,John Graham Construction Ltd,Causewayside Refurbishment,74806,Building Improvement 4 | 29/05/2017,Morris & Spottiswood Ltd,George IV Bridge Work,56448,Building Improvement 5 | 31/05/2017,John Graham Construction Ltd,Causewayside Refurbishment,164691,Building Improvement 6 | 24/07/2017,John Graham Construction Ltd,Causewayside Refurbishment,27926,Building Improvement 7 | 24/07/2017,John Graham Construction Ltd,Causewayside Refurbishment,212690,Building Improvement 8 | 16/08/2017,John Graham Construction Ltd,Causewayside Refurbishment,59021,Building Improvement 9 | 16/08/2017,John Graham Construction Ltd,Causewayside Refurbishment,136379,Building Improvement 10 | 23/08/2017,Culture And Sport Glasgow,Kelvin Hall,60503,Building Improvement 11 | 23/08/2017,XMA Scotland Ltd,Kelvin Hall,31830,Building Improvement 12 | 31/08/2017,John Graham Construction Ltd,Causewayside Refurbishment,36313,Building Improvement 13 | 31/08/2017,Insight Direct (UK) Ltd,Causewayside Refurbishment,68222,Building Improvement 14 | 31/08/2017,Mark Finn Laboratory,George IV Bridge Work,53884,Building Improvement 15 | 11/09/2017,John Graham Construction Ltd,Causewayside Refurbishment,189483,Building Improvement 16 | 23/10/2017,John Graham Construction Ltd,Causewayside Refurbishment,151659,Building Improvement 17 | 23/10/2017,City Building LLP,Causewayside Refurbishment,53147,Building Improvement 18 | 07/02/2017,John Graham Construction Ltd,Causewayside Refurbishment,52404,Building Improvement 19 | 13/02/2017,John Graham Construction Ltd,Causewayside Refurbishment,272390,Building Improvement 20 | 06/03/2017,John Graham Construction Ltd,Causewayside Refurbishment,31781,Building Improvement 21 | 06/03/2017,John Graham Construction Ltd,Causewayside Refurbishment,198048,Building Improvement 22 | 31/03/2017,Nicholson Bros(Electrical Contractors) Ltd,Causewayside Refurbishment,33666,Building Improvement 23 | 31/03/2017,John Graham Construction Ltd,Causewayside Refurbishment,222090,Building Improvement 24 | 31/03/2017,John Graham Construction Ltd,Causewayside Refurbishment,63971,Building Improvement 25 | 24/04/2017,Scottish Historic Buildings Trust,Lawnmarket Work,50057,Building Improvement 26 | 30/04/2017,Morris & Spottiswood Ltd,George IV Bridge Work,63716,Building Improvement 27 | 15/05/2017,John Graham Construction Ltd,Causewayside Refurbishment,245381,Building Improvement 28 | 12/09/2016,Flexiform,Kelvin Hall,42623,Building Improvement 29 | 12/09/2016,John Graham Construction Ltd,Causewayside Refurbishment,228689,Building Improvement 30 | 26/09/2016,Senator International,Kelvin Hall,35706,Building Improvement 31 | 26/09/2016,John Graham Construction Ltd,Causewayside Refurbishment,28378,Building Improvement 32 | 30/09/2016,A McGillivray,Causewayside Refurbishment,44392,Building Improvement 33 | 10/10/2016,John Graham Construction Ltd,Causewayside Refurbishment,303999,Building Improvement 34 | 31/10/2016,John Graham Construction Ltd,Causewayside Refurbishment,74245,Building Improvement 35 | 07/11/2016,CBRE,Kelvin Hall,83736,Building Improvement 36 | 14/11/2016,University Of Glasgow,Kelvin Hall,188682,Building Improvement 37 | 14/11/2016,John Graham Construction Ltd,Causewayside Refurbishment,362326,Building Improvement 38 | 12/12/2016,John Graham Construction Ltd,Causewayside Refurbishment,385310,Building Improvement 39 | 30/12/2016,John Graham Construction Ltd,Causewayside Refurbishment,253618,Building Improvement 40 | 30/12/2016,John Graham Construction Ltd,Causewayside Refurbishment,45127,Building Improvement 41 | 21/04/2016,M & J Ballantyne Ltd,George IV Bridge Work,35098,Building Improvement 42 | 09/05/2016,John Graham Construction Ltd,Causewayside Refurbishment,64361,Building Improvement 43 | 09/05/2016,A McGillivray,Causewayside Refurbishment,53690,Building Improvement 44 | 16/05/2016,John Graham Construction Ltd,Causewayside Refurbishment,365344,Building Improvement 45 | 10/06/2016,Wavetek Ltd,Kelvin Hall,87589,Building Improvement 46 | 10/06/2016,John Graham Construction Ltd,Causewayside Refurbishment,381803,Building Improvement 47 | 30/06/2016,Glasgow City Council,Kelvin Hall,1700000,Building Improvement 48 | 11/07/2016,Wavetek Ltd,Kelvin Hall,65692,Building Improvement 49 | 11/07/2016,John Graham Construction Ltd,Causewayside Refurbishment,139845,Building Improvement 50 | 25/07/2016,A McGillivray,Causewayside Refurbishment,30113,Building Improvement 51 | 15/08/2016,John Graham Construction Ltd,Causewayside Refurbishment,196807,Building Improvement 52 | 06/11/2017,John Graham Construction Ltd,Causewayside Refurbishment,134208,Building Improvement 53 | 31/03/2017,NLS Foundation,Grant Payment,177500,Other 54 | 09/10/2017,Frost And Sullivan Ltd,Literary & Archival Items,28125,Literature & Archive 55 | 09/10/2017,JISC Services Ltd ,Literary & Archival Items,43481,Literature & Archive 56 | 27/02/2017,Cengage Learning (Emea )Ltd,Literary & Archival Items,43302,Literature & Archive 57 | 06/03/2017,Private Sale,Literary & Archival Items,72500,Literature & Archive 58 | 31/03/2017,Private Sale,Literary & Archival Items,3422500,Literature & Archive 59 | 24/04/2017,Cengage Learning (Emea )Ltd,Literary & Archival Items,43302,Literature & Archive 60 | 22/05/2017,ALDL,Legal Deposit Services,27067,Literature & Archive 61 | 19/09/2016,Jisc Services Ltd Subscription Account,Literary & Archival Items,42629,Literature & Archive 62 | 10/10/2016,Cengage Learning (Emea )Ltd,Literary & Archival Items,86604,Literature & Archive 63 | 24/10/2016,ALDL,ALDL Charges,32317,Literature & Archive 64 | 26/04/2016,Private Sale,Literary & Archival Items,30000,Literature & Archive 65 | 30/05/2016,ALDL,ALDL Charges,32317,Literature & Archive 66 | 15/07/2016,Sotheby'S,Literary & Archival Items,28500,Literature & Archive 67 | 18/07/2016,Christies,Literary & Archival Items,33800,Literature & Archive 68 | 31/07/2016,ALDL,ALDL Charges,32317,Literature & Archive 69 | 08/12/2016,Sothebys,Literary & Archival Items,166000,Literature & Archive 70 | 08/12/2016,Private Sale,Literary & Archival Items,87500,Literature & Archive 71 | 26/06/2017,ECG Facilities Service,Facilities Management Charge,33386,Utility Bills 72 | 26/06/2017,British Library,Legal Deposit Services,50056,Other 73 | 24/07/2017,ALDL,Legal Deposit Services,27067,Other 74 | 16/08/2017,ECG Facilities Service,Facilities Management Charge,33386,Utility Bills 75 | 23/08/2017,ECG Facilities Service,Facilities Management Charge,33386,Utility Bills 76 | 07/02/2017,ECG Facilities Service,Facilities Management Charge,32795,Utility Bills 77 | 27/02/2017,ECG Facilities Service,Facilities Management Charge,32795,Utility Bills 78 | 27/03/2017,ECG Facilities Service,Facilities Management Charge,32795,Utility Bills 79 | 22/05/2017,ECG Facilities Service,Facilities Management Charge,33386,Utility Bills 80 | 26/09/2016,ECG Facilities Service,Facilities Management Charge,32795,Utility Bills 81 | 24/10/2016,ECG Facilities Service,Facilities Management Charge,32795,Utility Bills 82 | 08/12/2016,ECG Facilities Service,Facilities Management Charge,32795,Utility Bills 83 | 30/12/2016,ECG Facilities Service,Facilities Management Charge,32795,Utility Bills 84 | 23/05/2016,ECG Facilities Service,Facilities Management Charge,32777,Utility Bills 85 | 23/05/2016,ECG Facilities Service,Facilities Management Charge,32777,Utility Bills 86 | 28/06/2016,ECG Facilities Service,Facilities Management Charge,32832,Utility Bills 87 | 08/08/2016,ECG Facilities Service,Facilities Management Charge,32795,Utility Bills 88 | 24/08/2016,ECG Facilities Service,Facilities Management Charge,32795,Utility Bills 89 | 30/10/2017,ECG Facilities Service,Facilities Management Charge,35758,Utility Bills 90 | 16/08/2017,Ex Libris,IT equipment,76610,Software/IT 91 | 31/03/2017,XMA Scotland Ltd,IT equipment,33450,Software/IT 92 | 31/03/2017,XMA Scotland Ltd,IT equipment,84524,Software/IT 93 | 24/04/2017,Insight Direct (UK) Ltd,IT equipment,56768,Software/IT 94 | 09/05/2016,Computacenter Uk,Kelvin Hall,72835,Software/IT 95 | 23/05/2016,Computacenter Uk,Kelvin Hall,26506,Software/IT 96 | 15/09/2017,City Of Edinburgh Council,Non Domestic Rates ,57662,Utility Bills 97 | 15/09/2017,City Of Edinburgh Council,Non Domestic Rates ,142680,Utility Bills 98 | 08/05/2017,Anglian Water Business,Water,26832,Utility Bills 99 | 30/04/2016,City Of Edinburgh Council,Non Domestic Rates ,40800,Utility Bills 100 | 12/09/2016,City Of Edinburgh Council,Non Domestic Rates ,144330,Utility Bills 101 | 12/09/2016,City Of Edinburgh Council,Non Domestic Rates ,49827,Utility Bills 102 | 24/07/2017,AM Phillip,Vehicle Purchase,26604,Other -------------------------------------------------------------------------------- /examples/data/recommendations_embeddings_cache.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/examples/data/recommendations_embeddings_cache.pkl -------------------------------------------------------------------------------- /examples/fine-tuned_qa/answers_with_ft.py: -------------------------------------------------------------------------------- 1 | """ 2 | Note: To answer questions based on text documents, we recommend the procedure in 3 | [Question Answering using Embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Question_answering_using_embeddings.ipynb). 4 | Some of the code below may rely on [deprecated API endpoints](https://github.com/openai/openai-cookbook/tree/main/transition_guides_for_deprecated_API_endpoints). 5 | """ 6 | 7 | import argparse 8 | 9 | import openai 10 | 11 | 12 | def create_context( 13 | question, search_file_id, max_len=1800, search_model="ada", max_rerank=10 14 | ): 15 | """ 16 | Create a context for a question by finding the most similar context from the search file. 17 | :param question: The question 18 | :param search_file_id: The file id of the search file 19 | :param max_len: The maximum length of the returned context (in tokens) 20 | :param search_model: The search model to use 21 | :param max_rerank: The maximum number of reranking 22 | :return: The context 23 | """ 24 | results = openai.Engine(search_model).search( 25 | search_model=search_model, 26 | query=question, 27 | max_rerank=max_rerank, 28 | file=search_file_id, 29 | return_metadata=True, 30 | ) 31 | returns = [] 32 | cur_len = 0 33 | for result in results["data"]: 34 | cur_len += int(result["metadata"]) + 4 35 | if cur_len > max_len: 36 | break 37 | returns.append(result["text"]) 38 | return "\n\n###\n\n".join(returns) 39 | 40 | 41 | def answer_question( 42 | search_file_id="", 43 | fine_tuned_qa_model="", 44 | question="Which country won the European Football championship in 2021?", 45 | max_len=1800, 46 | search_model="ada", 47 | max_rerank=10, 48 | debug=False, 49 | stop_sequence=["\n", "."], 50 | max_tokens=100, 51 | ): 52 | """ 53 | Answer a question based on the most similar context from the search file, using your fine-tuned model. 54 | :param question: The question 55 | :param fine_tuned_qa_model: The fine tuned QA model 56 | :param search_file_id: The file id of the search file 57 | :param max_len: The maximum length of the returned context (in tokens) 58 | :param search_model: The search model to use 59 | :param max_rerank: The maximum number of reranking 60 | :param debug: Whether to output debug information 61 | :param stop_sequence: The stop sequence for Q&A model 62 | :param max_tokens: The maximum number of tokens to return 63 | :return: The answer 64 | """ 65 | context = create_context( 66 | question, 67 | search_file_id, 68 | max_len=max_len, 69 | search_model=search_model, 70 | max_rerank=max_rerank, 71 | ) 72 | if debug: 73 | print("Context:\n" + context) 74 | print("\n\n") 75 | try: 76 | # fine-tuned models requires model parameter, whereas other models require engine parameter 77 | model_param = ( 78 | {"model": fine_tuned_qa_model} 79 | if ":" in fine_tuned_qa_model 80 | and fine_tuned_qa_model.split(":")[1].startswith("ft") 81 | else {"engine": fine_tuned_qa_model} 82 | ) 83 | response = openai.Completion.create( 84 | prompt=f"Answer the question based on the context below\n\nText: {context}\n\n---\n\nQuestion: {question}\nAnswer:", 85 | temperature=0, 86 | max_tokens=max_tokens, 87 | top_p=1, 88 | frequency_penalty=0, 89 | presence_penalty=0, 90 | stop=stop_sequence, 91 | **model_param, 92 | ) 93 | return response["choices"][0]["text"] 94 | except Exception as e: 95 | print(e) 96 | return "" 97 | 98 | 99 | if __name__ == "__main__": 100 | parser = argparse.ArgumentParser( 101 | description="Rudimentary functionality of the answers endpoint with a fine-tuned Q&A model.", 102 | formatter_class=argparse.ArgumentDefaultsHelpFormatter, 103 | ) 104 | parser.add_argument( 105 | "--search_file_id", help="Search file id", required=True, type=str 106 | ) 107 | parser.add_argument( 108 | "--fine_tuned_qa_model", help="Fine-tuned QA model id", required=True, type=str 109 | ) 110 | parser.add_argument( 111 | "--question", help="Question to answer", required=True, type=str 112 | ) 113 | parser.add_argument( 114 | "--max_len", 115 | help="Maximum length of the returned context (in tokens)", 116 | default=1800, 117 | type=int, 118 | ) 119 | parser.add_argument( 120 | "--search_model", help="Search model to use", default="ada", type=str 121 | ) 122 | parser.add_argument( 123 | "--max_rerank", 124 | help="Maximum number of reranking for the search", 125 | default=10, 126 | type=int, 127 | ) 128 | parser.add_argument( 129 | "--debug", help="Print debug information (context used)", action="store_true" 130 | ) 131 | parser.add_argument( 132 | "--stop_sequence", 133 | help="Stop sequences for the Q&A model", 134 | default=["\n", "."], 135 | nargs="+", 136 | type=str, 137 | ) 138 | parser.add_argument( 139 | "--max_tokens", 140 | help="Maximum number of tokens to return", 141 | default=100, 142 | type=int, 143 | ) 144 | args = parser.parse_args() 145 | response = answer_question( 146 | search_file_id=args.search_file_id, 147 | fine_tuned_qa_model=args.fine_tuned_qa_model, 148 | question=args.question, 149 | max_len=args.max_len, 150 | search_model=args.search_model, 151 | max_rerank=args.max_rerank, 152 | debug=args.debug, 153 | stop_sequence=args.stop_sequence, 154 | max_tokens=args.max_tokens, 155 | ) 156 | print(f"Answer:{response}") 157 | -------------------------------------------------------------------------------- /images/chain_of_thought_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/chain_of_thought_fig1.png -------------------------------------------------------------------------------- /images/chain_of_thought_fig11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/chain_of_thought_fig11.png -------------------------------------------------------------------------------- /images/chain_of_thought_fig3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/chain_of_thought_fig3.png -------------------------------------------------------------------------------- /images/chain_of_thought_fig5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/chain_of_thought_fig5.png -------------------------------------------------------------------------------- /images/faithful-reasoning_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/faithful-reasoning_fig1.png -------------------------------------------------------------------------------- /images/faithful-reasoning_fig2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/faithful-reasoning_fig2.png -------------------------------------------------------------------------------- /images/faithful-reasoning_fig3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/faithful-reasoning_fig3.png -------------------------------------------------------------------------------- /images/faithful-reasoning_fig4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/faithful-reasoning_fig4.png -------------------------------------------------------------------------------- /images/faithful-reasoning_fig5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/faithful-reasoning_fig5.png -------------------------------------------------------------------------------- /images/faithful-reasoning_fig7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/faithful-reasoning_fig7.png -------------------------------------------------------------------------------- /images/faithful-reasoning_tab2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/faithful-reasoning_tab2.png -------------------------------------------------------------------------------- /images/faithful-reasoning_tab5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/faithful-reasoning_tab5.png -------------------------------------------------------------------------------- /images/least-to-most_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/least-to-most_fig1.png -------------------------------------------------------------------------------- /images/least-to-most_tab11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/least-to-most_tab11.png -------------------------------------------------------------------------------- /images/least-to-most_tab4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/least-to-most_tab4.png -------------------------------------------------------------------------------- /images/least-to-most_tab9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/least-to-most_tab9.png -------------------------------------------------------------------------------- /images/lm_cascades_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/lm_cascades_fig1.png -------------------------------------------------------------------------------- /images/lm_cascades_fig3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/lm_cascades_fig3.png -------------------------------------------------------------------------------- /images/lm_cascades_fig4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/lm_cascades_fig4.png -------------------------------------------------------------------------------- /images/lm_cascades_fig5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/lm_cascades_fig5.png -------------------------------------------------------------------------------- /images/lm_cascades_fig6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/lm_cascades_fig6.png -------------------------------------------------------------------------------- /images/maieutic_fig2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/maieutic_fig2.png -------------------------------------------------------------------------------- /images/maieutic_fig6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/maieutic_fig6.png -------------------------------------------------------------------------------- /images/maieutic_tab1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/maieutic_tab1.png -------------------------------------------------------------------------------- /images/selection-inference_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/selection-inference_fig1.png -------------------------------------------------------------------------------- /images/selection-inference_fig4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/selection-inference_fig4.png -------------------------------------------------------------------------------- /images/self-consistency_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/self-consistency_fig1.png -------------------------------------------------------------------------------- /images/self-consistency_fig3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/self-consistency_fig3.png -------------------------------------------------------------------------------- /images/star_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/star_fig1.png -------------------------------------------------------------------------------- /images/star_tab1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/star_tab1.png -------------------------------------------------------------------------------- /images/verifiers_fig3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/verifiers_fig3.png -------------------------------------------------------------------------------- /images/verifiers_fig5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/verifiers_fig5.png -------------------------------------------------------------------------------- /images/zero-shot_reasoners_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/zero-shot_reasoners_fig1.png -------------------------------------------------------------------------------- /images/zero-shot_reasoners_fig2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/zero-shot_reasoners_fig2.png -------------------------------------------------------------------------------- /images/zero-shot_reasoners_tab1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/zero-shot_reasoners_tab1.png -------------------------------------------------------------------------------- /images/zero-shot_reasoners_tab5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisasari/openai-cookbook/562055dd8a87c752de473b3f752a283b7686fa3e/images/zero-shot_reasoners_tab5.png -------------------------------------------------------------------------------- /transition_guides_for_deprecated_API_endpoints/README.md: -------------------------------------------------------------------------------- 1 | # Deprecation of Answers, Classification, and Search 2 | 3 | In 2021, OpenAI released specialized endpoints in beta for Answers, Classification, and Search. 4 | 5 | While these specialized endpoints were convenient, they had two drawbacks: 6 | 7 | 1. These specialized endpoints were eclipsed by techniques that achieved better results. 8 | 2. These specialized endpoints were more difficult to customize and optimize for individual use cases. 9 | 10 | As a result, **the Answers, Classifications, and Search endpoints are being deprecated.** 11 | 12 | ## Timeline of deprecation 13 | 14 | For those who have not used these endpoints, nothing will change except that access will no longer be available. 15 | 16 | **For existing users of these endpoints, access will continue until December 3, 2022.** Before that date, we strongly encourage developers to switch over to newer techniques which produce better results. 17 | 18 | ## How to transition 19 | 20 | We've written guides and code examples for transitioning from the deprecated API endpoints to better methods. 21 | 22 | ### Answers 23 | 24 | [Guide: How to transition off the Answers endpoint](https://help.openai.com/en/articles/6233728-answers-transition-guide) 25 | 26 | * Option 1: transition to embeddings-based search **(recommended)** 27 | * Example code: [Semantic_text_search_using_embeddings.ipynb](../examples/Semantic_text_search_using_embeddings.ipynb) 28 | 29 | * Option 2: reimplement Answers endpoint functionality 30 | * Example code: [answers_functionality_example.py](answers_functionality_example.py) 31 | 32 | ### Classification 33 | 34 | [Guide: How to transition off the Classifications endpoint](https://help.openai.com/en/articles/6272941-classifications-transition-guide) 35 | 36 | * Option 1: transition to fine-tuning **(recommended)** 37 | * Example code: [Fine-tuned_classification.ipynb](../examples/Fine-tuned_classification.ipynb) 38 | * Option 2: transition to embeddings 39 | * Example code: [Semantic_text_search_using_embeddings.ipynb](../examples/Semantic_text_search_using_embeddings.ipynb) 40 | * Option 3: reimplement Classifications endpoint functionality 41 | * Example code: [classification_functionality_example.py](classification_functionality_example.py) 42 | 43 | ### Search 44 | 45 | [Guide: How to transition off the Search endpoint](https://help.openai.com/en/articles/6272952-search-transition-guide) 46 | 47 | * Option 1: transition to embeddings-based search **(recommended)** 48 | * Example code: [Semantic_text_search_using_embeddings.ipynb](../examples/Semantic_text_search_using_embeddings.ipynb) 49 | * Option 2: reimplement Search endpoint functionality 50 | * Example code: [search_functionality_example.py](search_functionality_example.py) 51 | -------------------------------------------------------------------------------- /transition_guides_for_deprecated_API_endpoints/answers_functionality_example.py: -------------------------------------------------------------------------------- 1 | from transformers import GPT2TokenizerFast 2 | 3 | import openai 4 | 5 | tokenizer = GPT2TokenizerFast.from_pretrained("gpt2") 6 | 7 | MAX_TOKENS_LIMIT = 2048 8 | ANSWERS_INSTRUCTION = "Please answer the question according to the above context.\n" 9 | CONTEXT_TEMPLATE = "===\nContext: {context}\n===\n" 10 | 11 | 12 | def extract_instruction(instruction): 13 | """ 14 | Extract `instruction` parameter and format it properly. 15 | If not exist, return empty string. 16 | """ 17 | if instruction is None: 18 | return "" 19 | 20 | return f"{instruction.strip()}\n\n" 21 | 22 | 23 | def semantic_search( 24 | search_model, query_for_search, file_id=None, max_documents=None, examples=None 25 | ): 26 | """ 27 | :param examples: A list of {"text":...} or {"text": ..., "label": ...}. 28 | :return: 29 | a list of semantic search result dict of documents sorted by "score": 30 | [ 31 | { 32 | "document": ..., 33 | "object": "search_result", 34 | "score": ..., 35 | "text": ..., 36 | }, 37 | ... 38 | ] 39 | """ 40 | assert (examples is None) ^ (file_id is None) # xor 41 | 42 | if file_id is not None: 43 | # This is where you'd do an elastic search call. Since there isn't an example of this 44 | # we can query, we'll raise an error. 45 | # The return value from this would be a list of examples 46 | raise NotImplementedError() 47 | 48 | # This isn't quite accurate since Search is also being deprecated. See our search guide for more 49 | # information. 50 | 51 | search_result = openai.Search.create( 52 | model=search_model, 53 | documents=[x["text"] for x in examples], 54 | query=query_for_search, 55 | ) 56 | 57 | info_dict = {d["document"]: d for d in search_result["data"]} 58 | sorted_doc_ids = sorted( 59 | info_dict.keys(), key=lambda x: info_dict[x]["score"], reverse=True 60 | ) 61 | if max_documents: 62 | sorted_doc_ids = sorted_doc_ids[:max_documents] 63 | return [info_dict[i] for i in sorted_doc_ids] 64 | 65 | 66 | def select_by_length( 67 | sorted_doc_infos, 68 | max_token_len, 69 | lambda_fn=None, 70 | ): 71 | """ 72 | Give a list of (document ID, document content in string), we will select as many 73 | documents as possible as long as the total length does not go above `max_token_len`. 74 | 75 | :param sorted_doc_infos: A list of semantic search result dict of documents sorted by "score". 76 | :param max_token_len: The maximum token length for selected documents. 77 | :param lambda_fn: A function that takes in search results dict and output a formatted 78 | example for context stuffing. 79 | :return: A tuple of ( 80 | A concatenation of selected documents used as context, 81 | A list of selected document IDs 82 | ) 83 | """ 84 | if not sorted_doc_infos: 85 | return "", [] 86 | 87 | selected_indices = [] 88 | total_doc_tokens = 0 89 | doc_dict = {} 90 | for i, doc_info in enumerate(sorted_doc_infos): 91 | doc = lambda_fn(doc_info) if lambda_fn else doc_info["text"] 92 | n_doc_tokens = len(tokenizer.encode(doc)) 93 | if total_doc_tokens + n_doc_tokens < max_token_len: 94 | total_doc_tokens += n_doc_tokens 95 | selected_indices.append(i) 96 | doc_dict[i] = doc 97 | 98 | # The top ranked documents should go at the end. 99 | selected_indices = selected_indices[::-1] 100 | 101 | context = "".join([doc_dict[i] for i in selected_indices]) 102 | selected_doc_infos = [sorted_doc_infos[i] for i in selected_indices] 103 | return context, selected_doc_infos 104 | 105 | 106 | def answers( 107 | examples, 108 | question, 109 | model, 110 | examples_context, 111 | file_id=None, 112 | documents=None, 113 | logit_bias=None, 114 | max_rerank=200, 115 | max_tokens=16, 116 | alternative_question=None, 117 | search_model="ada", 118 | temperature=0.0, 119 | logprobs=0, 120 | stop=None, 121 | n=1, 122 | ): 123 | """ 124 | Given a prompt, a question, a list of (question, answer) pairs as examples, and 125 | a list of documents for context, it tries to include all the QA examples and top 126 | relevant context documents. 127 | 128 | The constructed prompt for the final completion call: 129 | ``` 130 | Please answer the question according to the above context. 131 | 132 | === 133 | Context: {{ the context for example QA pairs. }} 134 | === 135 | Q: example 1 question 136 | A: example 1 answer 137 | --- 138 | Q: example 2 question 139 | A: example 2 answer 140 | === 141 | Context: {{ a list of relevant documents sorted via search(question, documents) }} 142 | === 143 | Q: question 144 | A: 145 | ``` 146 | 147 | The returned object has a structure like: 148 | { 149 | "answers": [ 150 | "Beijing", 151 | "Beijing, China" 152 | ], 153 | "completion_id": "xxx-xxx", 154 | "object": "answer", 155 | "selected_documents": [ 156 | { 157 | "document": ..., # document index, same as in search/ results. 158 | "object": "search_result", 159 | "text": ..., 160 | }, 161 | ... 162 | ], 163 | } 164 | """ 165 | 166 | examples = examples if examples else [] 167 | 168 | example_prompts = [f"Q: {x}\nA: {y}" for x, y in examples] 169 | prompt = f"Q: {question}\nA:" 170 | 171 | # Append all the QA examples into the prompt. 172 | if examples_context: 173 | examples_context = CONTEXT_TEMPLATE.format(context=examples_context) 174 | instruction = ( 175 | ANSWERS_INSTRUCTION + examples_context + "\n---\n".join(example_prompts) + "\n" 176 | ) 177 | 178 | logit_bias = logit_bias if logit_bias is not None else {} 179 | 180 | if file_id is None and documents is None: 181 | raise Exception("Please submit at least one of `documents` or `file`.") 182 | if file_id is not None and documents is not None: 183 | raise Exception("Please submit only one of `documents` or `file`.") 184 | 185 | instruction = extract_instruction(instruction) 186 | 187 | n_instruction_tokens = len(tokenizer.encode(instruction)) 188 | n_prompt_tokens = len(tokenizer.encode(prompt)) 189 | n_query_tokens = len(tokenizer.encode(question)) 190 | n_context_tokens = len(tokenizer.encode(CONTEXT_TEMPLATE.format(context=""))) 191 | 192 | if documents is not None: 193 | documents = [doc.strip() + " " for doc in documents] 194 | n_docs_tokens = [len(tokenizer.encode(doc)) for doc in documents] 195 | 196 | # Except all the required content, how many tokens left for context stuffing. 197 | leftover_token_len = MAX_TOKENS_LIMIT - ( 198 | n_instruction_tokens + n_context_tokens + n_prompt_tokens + max_tokens 199 | ) 200 | sorted_doc_infos = [] 201 | 202 | question_for_search = ( 203 | alternative_question if alternative_question is not None else question 204 | ) 205 | if file_id is not None: 206 | search_model_, sorted_doc_infos = semantic_search( 207 | search_model, 208 | question_for_search, 209 | file_id=file_id, 210 | max_documents=max_rerank, 211 | ) 212 | 213 | elif len(documents) == 0: 214 | # If no context document is provided, do nothing. 215 | pass 216 | 217 | elif min(n_docs_tokens) >= leftover_token_len: 218 | # If there is no room for adding any context doc. 219 | pass 220 | 221 | elif (max_rerank is None or max_rerank >= len(documents)) and sum( 222 | n_docs_tokens 223 | ) < leftover_token_len: 224 | # If the total length of docs is short enough to be added all. 225 | selected_indices = list(range(len(documents))) 226 | 227 | sorted_doc_infos = [ 228 | {"document": i, "text": documents[i]} for i in selected_indices 229 | ] 230 | 231 | elif n_query_tokens + max(n_docs_tokens) >= MAX_TOKENS_LIMIT: 232 | # If the prompt and the longest document together go above the limit. 233 | total_tokens = n_query_tokens + max(n_docs_tokens) 234 | raise Exception( 235 | f"The longest document and prompt pair together contains {total_tokens} " 236 | f"tokens, above the limit {MAX_TOKENS_LIMIT} for semantic search. Please consider " 237 | f"shortening the prompt or the longest document." 238 | ) 239 | 240 | else: 241 | # If we can add some context documents but not all of them, we should 242 | # query search endpoint to rank docs by score. 243 | sorted_doc_infos = semantic_search( 244 | search_model, 245 | question_for_search, 246 | examples=[{"text": doc} for doc in documents], 247 | max_documents=max_rerank, 248 | ) 249 | 250 | # Select documents w.r.t. the context length limitation. 251 | context, sorted_doc_infos = select_by_length( 252 | sorted_doc_infos, 253 | leftover_token_len, 254 | lambda_fn=lambda x: x["text"].strip() + " ", 255 | ) 256 | 257 | # Add instruction before the context and the prompt after the context. 258 | if context: 259 | context = CONTEXT_TEMPLATE.format(context=context.strip()) 260 | full_prompt = instruction + context + prompt 261 | 262 | completion_result = openai.Completion.create( 263 | engine=model, 264 | prompt=full_prompt, 265 | logit_bias=logit_bias, 266 | temperature=temperature, 267 | n=n, 268 | max_tokens=max_tokens, 269 | stop=stop, 270 | logprobs=logprobs, 271 | ) 272 | 273 | completion_result["selected_documents"] = sorted_doc_infos 274 | 275 | result = dict( 276 | object="answer", 277 | selected_documents=completion_result.pop("selected_documents"), 278 | completion=completion_result["id"], 279 | ) 280 | 281 | result["answers"] = [ 282 | item["text"].replace("A:", "").split("Q:")[0].strip() 283 | for item in completion_result["choices"] 284 | ] 285 | 286 | return result 287 | 288 | 289 | print( 290 | answers( 291 | examples=[ 292 | ["What is the capital of Washington", "Olympia"], 293 | ["What is the capital of Oregon", "Salem"], 294 | ], 295 | question="What is the capital of China?", 296 | examples_context="I am a bot that names country capitals", 297 | documents=["I am a bot that names country capitals"], 298 | model="davinci", 299 | search_model="ada", 300 | alternative_question="different test", 301 | max_tokens=16, 302 | stop=["\n\n"], 303 | ) 304 | ) 305 | -------------------------------------------------------------------------------- /transition_guides_for_deprecated_API_endpoints/classification_functionality_example.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | from collections import defaultdict 3 | 4 | from transformers import GPT2TokenizerFast 5 | 6 | import openai 7 | 8 | tokenizer = GPT2TokenizerFast.from_pretrained("gpt2") 9 | 10 | MAX_TOKENS_LIMIT = 2048 11 | 12 | 13 | def create_instruction(labels) -> str: 14 | """ 15 | Construct an instruction for a classification task. 16 | """ 17 | instruction = f"Please classify a piece of text into the following categories: {', '.join(labels)}." 18 | 19 | return f"{instruction.strip()}\n\n" 20 | 21 | 22 | def semantic_search( 23 | search_model, query_for_search, file_id=None, max_documents=None, examples=None 24 | ): 25 | """ 26 | :param examples: A list of {"text":...} or {"text": ..., "label": ...}. 27 | :return: 28 | a list of semantic search result dict of documents sorted by "score": 29 | [ 30 | { 31 | "document": ..., 32 | "object": "search_result", 33 | "score": ..., 34 | "text": ..., 35 | }, 36 | ... 37 | ] 38 | 39 | """ 40 | assert (examples is None) ^ (file_id is None) # xor 41 | 42 | if file_id is not None: 43 | # This is where you'd do an elastic search call. Since there isn't an example of this 44 | # we can query, we'll raise an error. 45 | # The return value from this would be a list of examples 46 | raise NotImplementedError() 47 | 48 | # This isn't quite accurate since Search is also being deprecated. See our search guide for more 49 | # information. 50 | 51 | search_result = openai.Search.create( 52 | model=search_model, 53 | documents=[x["text"] for x in examples], 54 | query=query_for_search, 55 | ) 56 | 57 | info_dict = {d["document"]: d for d in search_result["data"]} 58 | sorted_doc_ids = sorted( 59 | info_dict.keys(), key=lambda x: info_dict[x]["score"], reverse=True 60 | ) 61 | if max_documents: 62 | sorted_doc_ids = sorted_doc_ids[:max_documents] 63 | return [info_dict[i] for i in sorted_doc_ids] 64 | 65 | 66 | def select_by_length( 67 | sorted_doc_infos, 68 | max_token_len, 69 | lambda_fn=None, 70 | ): 71 | """ 72 | Give a list of (document ID, document content in string), we will select as many 73 | documents as possible as long as the total length does not go above `max_token_len`. 74 | 75 | :param sorted_doc_infos: A list of semantic search result dict of documents sorted by "score". 76 | :param max_token_len: The maximum token length for selected documents. 77 | :param lambda_fn: A function that takes in search results dict and output a formatted 78 | example for context stuffing. 79 | :return: A tuple of ( 80 | A concatenation of selected documents used as context, 81 | A list of selected document IDs 82 | ) 83 | """ 84 | if not sorted_doc_infos: 85 | return "", [] 86 | 87 | selected_indices = [] 88 | total_doc_tokens = 0 89 | doc_dict = {} 90 | for i, doc_info in enumerate(sorted_doc_infos): 91 | doc = lambda_fn(doc_info) if lambda_fn else doc_info["text"] 92 | n_doc_tokens = len(tokenizer.encode(doc)) 93 | if total_doc_tokens + n_doc_tokens < max_token_len: 94 | total_doc_tokens += n_doc_tokens 95 | selected_indices.append(i) 96 | doc_dict[i] = doc 97 | 98 | # The top ranked documents should go at the end. 99 | selected_indices = selected_indices[::-1] 100 | 101 | context = "".join([doc_dict[i] for i in selected_indices]) 102 | selected_doc_infos = [sorted_doc_infos[i] for i in selected_indices] 103 | return context, selected_doc_infos 104 | 105 | 106 | def format_example_fn(x: dict) -> str: 107 | return "Text: {text}\nCategory: {label}\n---\n".format( 108 | text=x["text"].replace("\n", " ").strip(), 109 | label=x["label"].replace("\n", " ").strip(), 110 | ) 111 | 112 | 113 | def classifications( 114 | query, 115 | model, 116 | search_model="ada", 117 | examples=None, 118 | file=None, 119 | labels=None, 120 | temperature=0.0, 121 | logprobs=None, 122 | max_examples=200, 123 | logit_bias=None, 124 | alternative_query=None, 125 | max_tokens=16, 126 | ) -> dict: 127 | """ 128 | Given a prompt, a question and a list of examples, containing (text, label) pairs, 129 | it selects top relevant examples to construct a prompt for few-shot classification. 130 | 131 | The constructed prompt for the final completion call: 132 | ``` 133 | {{ an optional instruction }} 134 | 135 | Text: example 1 text 136 | Category: example 1 label 137 | --- 138 | Text: example 1 text 139 | Category: example 2 label 140 | --- 141 | Text: question 142 | Category: 143 | ``` 144 | 145 | The returned object has a structure like: 146 | { 147 | "label": "Happy", 148 | "model": "ada", 149 | "object": "classification", 150 | "selected_examples": [ 151 | { 152 | "document": ..., # document index, same as in search/ results. 153 | "text": ..., 154 | "label": ..., 155 | }, 156 | ... 157 | ], 158 | } 159 | """ 160 | 161 | query = query.replace("\n", " ").strip() 162 | logit_bias = logit_bias if logit_bias else {} 163 | labels = labels if labels else [] 164 | 165 | if file is None and examples is None: 166 | raise Exception("Please submit at least one of `examples` or `file`.") 167 | if file is not None and examples is not None: 168 | raise Exception("Please submit only one of `examples` or `file`.") 169 | 170 | instruction = create_instruction(labels) 171 | 172 | query_for_search = alternative_query if alternative_query is not None else query 173 | 174 | # Extract examples and example labels first. 175 | if file is not None: 176 | sorted_doc_infos = semantic_search( 177 | search_model, 178 | query_for_search, 179 | file_id=file, 180 | max_documents=max_examples, 181 | ) 182 | 183 | else: 184 | example_prompts = [ 185 | format_example_fn(dict(text=x, label=y)) for x, y in examples 186 | ] 187 | n_examples_tokens = [len(tokenizer.encode(x)) for x in example_prompts] 188 | 189 | query_prompt = f"Text: {query}\nCategory:" 190 | n_instruction_tokens = len(tokenizer.encode(instruction)) 191 | n_query_tokens = len(tokenizer.encode(query_prompt)) 192 | 193 | # Except all the required content, how many tokens left for context stuffing. 194 | leftover_token_len = MAX_TOKENS_LIMIT - ( 195 | n_instruction_tokens + n_query_tokens + max_tokens 196 | ) 197 | 198 | # Process when `examples` are provided but no `file` is provided. 199 | if examples: 200 | if (max_examples is None or max_examples >= len(examples)) and sum( 201 | n_examples_tokens 202 | ) < leftover_token_len: 203 | # If the total length of docs is short enough that we can add all examples, no search call. 204 | selected_indices = list(range(len(examples))) 205 | 206 | sorted_doc_infos = [ 207 | {"document": i, "text": examples[i][0], "label": examples[i][1]} 208 | for i in selected_indices 209 | ] 210 | 211 | elif max(n_examples_tokens) + n_query_tokens >= MAX_TOKENS_LIMIT: 212 | # If the prompt and the longest example together go above the limit: 213 | total_tokens = max(n_examples_tokens) + n_query_tokens 214 | raise Exception( 215 | user_message=f"The longest classification example, query and prompt together contain " 216 | f"{total_tokens} tokens, above the limit {MAX_TOKENS_LIMIT} for semantic search. " 217 | f"Please consider shortening your instruction, query or the longest example." 218 | ) 219 | 220 | else: 221 | # If we can add some context documents but not all of them, we should 222 | # query search endpoint to rank docs by score. 223 | sorted_doc_infos = semantic_search( 224 | search_model, 225 | query_for_search, 226 | examples=[{"text": x, "label": y} for x, y in examples], 227 | max_documents=max_examples, 228 | ) 229 | 230 | # Per label, we have a list of doc id sorted by its relevancy to the query. 231 | label_to_indices = defaultdict(list) 232 | for idx, d in enumerate(sorted_doc_infos): 233 | label_to_indices[d["label"]].append(idx) 234 | 235 | # Do a round robin for each of the different labels, taking the best match for each label. 236 | label_indices = [label_to_indices[label] for label in labels] 237 | mixed_indices = [ 238 | i for x in itertools.zip_longest(*label_indices) for i in x if i is not None 239 | ] 240 | sorted_doc_infos = [sorted_doc_infos[i] for i in mixed_indices] 241 | 242 | # Try to select as many examples as needed to fit into the context 243 | context, sorted_doc_infos = select_by_length( 244 | sorted_doc_infos, 245 | leftover_token_len, 246 | lambda_fn=format_example_fn, 247 | ) 248 | 249 | prompt = instruction + context + query_prompt 250 | 251 | completion_params = { 252 | "engine": model, 253 | "prompt": prompt, 254 | "temperature": temperature, 255 | "logprobs": logprobs, 256 | "logit_bias": logit_bias, 257 | "max_tokens": max_tokens, 258 | "stop": "\n", 259 | "n": 1, 260 | } 261 | 262 | completion_resp = openai.Completion.create( 263 | **completion_params, 264 | ) 265 | 266 | label = completion_resp["choices"][0]["text"] 267 | label = label.split("\n")[0].strip().lower().capitalize() 268 | if label not in labels: 269 | label = "Unknown" 270 | 271 | result = dict( 272 | # TODO: Add id for object persistence. 273 | object="classification", 274 | model=completion_resp["model"], 275 | label=label, 276 | completion=completion_resp["id"], 277 | ) 278 | 279 | result["selected_examples"] = sorted_doc_infos 280 | 281 | return result 282 | 283 | 284 | print( 285 | classifications( 286 | query="this is my test", 287 | model="davinci", 288 | search_model="ada", 289 | examples=[ 290 | ["this is my test", "davinci"], 291 | ["this is other test", "blahblah"], 292 | ], 293 | file=None, 294 | labels=["davinci", "blahblah"], 295 | temperature=0.1, 296 | logprobs=0, 297 | max_examples=200, 298 | logit_bias=None, 299 | alternative_query="different test", 300 | max_tokens=16, 301 | ) 302 | ) 303 | -------------------------------------------------------------------------------- /transition_guides_for_deprecated_API_endpoints/search_functionality_example.py: -------------------------------------------------------------------------------- 1 | from transformers import GPT2TokenizerFast 2 | 3 | import openai 4 | 5 | tokenizer = GPT2TokenizerFast.from_pretrained("gpt2") 6 | 7 | docs = ["test1", "asdklgjnasdv", "banana", "lord lollipop"] 8 | query = "apple orang asdansbdausd" 9 | 10 | print(openai.Search.create(model="davinci", query=query, documents=docs)) 11 | 12 | 13 | def construct_context(query, document): 14 | return "<|endoftext|>{document}\n\n---\n\nThe above passage is related to: {query}".format( 15 | document=document, query=query 16 | ) 17 | 18 | 19 | def get_score(context, query, log_probs, text_offsets) -> float: 20 | SCORE_MULTIPLIER = 100.0 21 | 22 | log_prob = 0 23 | count = 0 24 | cutoff = len(context) - len(query) 25 | 26 | for i in range(len(text_offsets) - 1, 0, -1): 27 | log_prob += log_probs[i] 28 | count += 1 29 | 30 | if text_offsets[i] <= cutoff and text_offsets[i] != text_offsets[i - 1]: 31 | break 32 | 33 | return log_prob / float(count) * SCORE_MULTIPLIER 34 | 35 | 36 | def search(query, documents, engine): 37 | 38 | prompts = [construct_context(query, doc) for doc in [""] + documents] 39 | 40 | resps = openai.Completion.create( 41 | model=engine, 42 | prompt=prompts, 43 | temperature=1.0, 44 | top_p=1.0, 45 | max_tokens=0, 46 | logprobs=0, 47 | n=1, 48 | echo=True, 49 | ) 50 | 51 | resps_by_index = {choice["index"]: choice for choice in resps["choices"]} 52 | 53 | scores = [ 54 | get_score( 55 | prompts[i], 56 | query, 57 | resps_by_index[i]["logprobs"]["token_logprobs"], 58 | resps_by_index[i]["logprobs"]["text_offset"], 59 | ) 60 | for i in range(len(prompts)) 61 | ] 62 | 63 | # Process results 64 | scores = [score - scores[0] for score in scores][1:] 65 | 66 | return [ 67 | { 68 | "object": "search_result", 69 | "document": document_idx, 70 | "score": round(score, 3), 71 | } 72 | for document_idx, score in enumerate(scores) 73 | ] 74 | 75 | 76 | print(search(query=query, documents=docs, engine="davinci")) 77 | --------------------------------------------------------------------------------