Merge branch 'main' into james/sdk_updates

pull/836/head
jhills20 3 months ago
commit 98d2e4feb8

@ -31,16 +31,18 @@ For additional advice on writing good documentation, refer to [What Makes Docume
| Uniqueness | Does the content offer new insights or unique information compared to existing documentation? | |
| Clarity | Is the language easy to understand? Are things well-explained? Is the title clear? | |
| Correctness | Are the facts, code snippets, and examples correct and reliable? Does everything execute correctly? | |
| Conciseness | Is the content concise? Are all details necessary? Can it be made shorter? | |
| Completeness | Is the content thorough and detailed? Are there things that werent explained fully? | |
| Grammar | Are there grammatical or spelling errors present? | |
### Breakdown
| Criteria | 4 | 3 | 2 | 1 |
| ------------ | -------------------------------------- | ---------------------------------------- | --------------------------------------------- | ------------------------------------- |
| Relevance | Relevant and useful. | Relevant but not very useful. | Tangentially relevant. | Not relevant. |
| Uniqueness | Completely unique with fresh insights. | Unique with minor overlaps. | Some unique aspects, but significant overlap. | Many similar guides/examples. |
| Clarity | Clear language and structure. | Clear language, unclear structure. | Some sections unclear. | Confusing and unclear. |
| Correctness | Completely error free. | Code works, minor improvements needed. | Few errors and warnings. | Many errors, code doesn't execute. |
| Completeness | Complete and detailed. | Mostly complete, minor additions needed. | Lacks some explanations. | Missing significant portions. |
| Grammar | Perfect grammar. | Correct grammar, few typos. | Some spelling/grammatical errors. | Numerous spelling/grammatical errors. |
| Criteria | 4 | 3 | 2 | 1 |
| ------------ | --------------------------------------------- | ----------------------------------------- | --------------------------------------------- | ------------------------------------------ |
| Relevance | Relevant and useful. | Relevant but not very useful. | Tangentially relevant. | Not relevant. |
| Uniqueness | Completely unique with fresh insights. | Unique with minor overlaps. | Some unique aspects, but significant overlap. | Many similar guides/examples. |
| Clarity | Clear language and structure. | Clear language, unclear structure. | Some sections unclear. | Confusing and unclear. |
| Correctness | Completely error free. | Code works, minor improvements needed. | Few errors and warnings. | Many errors, code doesn't execute. |
| Conciseness | Cannot be reduced in any section, or overall. | Mostly short, but could still be reduced. | Some long sections, and/or long overall. | Very long sections and overall, redundant. |
| Completeness | Complete and detailed. | Mostly complete, minor additions needed. | Lacks some explanations. | Missing significant portions. |
| Grammar | Perfect grammar. | Correct grammar, few typos. | Some spelling/grammatical errors. | Numerous spelling/grammatical errors. |

@ -6,18 +6,18 @@
The magic of large language models is that by being trained to minimize this prediction error over vast quantities of text, the models end up learning concepts useful for these predictions. For example, they learn:
* how to spell
* how grammar works
* how to paraphrase
* how to answer questions
* how to hold a conversation
* how to write in many languages
* how to code
* etc.
- how to spell
- how grammar works
- how to paraphrase
- how to answer questions
- how to hold a conversation
- how to write in many languages
- how to code
- etc.
None of these capabilities are explicitly programmed in—they all emerge as a result of training.
They do this by “reading” a large amount of existing text and learning how words tend to appear in context with other words, and uses what it has learned to predict the next most likely word that might appear in response to a user request, and each subsequent word after that.
GPT-3 powers [hundreds of software products][GPT3 Apps Blog Post], including productivity apps, education apps, games, and more.
GPT-3 and GPT-4 power [many software products][OpenAI Customer Stories], including productivity apps, education apps, games, and more.
## How to control a large language model
@ -25,17 +25,18 @@ Of all the inputs to a large language model, by far the most influential is the
Large language models can be prompted to produce output in a few ways:
* **Instruction**: Tell the model what you want
* **Completion**: Induce the model to complete the beginning of what you want
* **Demonstration**: Show the model what you want, with either:
* A few examples in the prompt
* Many hundreds or thousands of examples in a fine-tuning training dataset
- **Instruction**: Tell the model what you want
- **Completion**: Induce the model to complete the beginning of what you want
- **Scenario**: Give the model a situation to play out
- **Demonstration**: Show the model what you want, with either:
- A few examples in the prompt
- Many hundreds or thousands of examples in a fine-tuning training dataset
An example of each is shown below.
### Instruction prompts
Instruction-following models (e.g., `text-davinci-003` or any model beginning with `text-`) are specially designed to follow instructions. Write your instruction at the top of the prompt (or at the bottom, or both), and the model will do its best to follow the instruction and then stop. Instructions can be detailed, so don't be afraid to write a paragraph explicitly detailing the output you want.
Write your instruction at the top of the prompt (or at the bottom, or both), and the model will do its best to follow the instruction and then stop. Instructions can be detailed, so don't be afraid to write a paragraph explicitly detailing the output you want, just stay aware of how many [tokens](https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them) the model can process.
Example instruction prompt:
@ -71,6 +72,25 @@ Output:
Ted Chiang
```
### Scenario prompt example
Giving the model a scenario to follow or role to play out can be helpful for complex queries or when seeking imaginative responses. When using a hypothetical prompt, you set up a situation, problem, or story, and then ask the model to respond as if it were a character in that scenario or an expert on the topic.
Example scenario prompt:
```text
Your role is to extract the name of the author from any given text
“Some humans theorize that intelligent species go extinct before they can expand into outer space. If they're correct, then the hush of the night sky is the silence of the graveyard.”
― Ted Chiang, Exhalation
```
Output:
```text
Ted Chiang
```
### Demonstration prompt example (few-shot learning)
Similar to completion-style prompts, demonstrations can show the model what you want it to do. This approach is sometimes called few-shot learning, as the model learns from a few examples provided in the prompt.
@ -118,17 +138,15 @@ Output:
## Code Capabilities
Large language models aren't only great at text - they can be great at code too. OpenAI's specialized code model is called [Codex].
Large language models aren't only great at text - they can be great at code too. OpenAI's [GPT-4][GPT-4 and GPT-4 Turbo] model is a prime example.
Codex powers [more than 70 products][Codex Apps Blog Post], including:
GPT-4 powers [numerous innovative products][OpenAI Customer Stories], including:
* [GitHub Copilot] (autocompletes code in VS Code and other IDEs)
* [Pygma](https://pygma.app/) (turns Figma designs into code)
* [Replit](https://replit.com/) (has an 'Explain code' button and other features)
* [Warp](https://www.warp.dev/) (a smart terminal with AI command search)
* [Machinet](https://machinet.net/) (writes Java unit test templates)
- [GitHub Copilot] (autocompletes code in Visual Studio and other IDEs)
- [Replit](https://replit.com/) (can complete, explain, edit and generate code)
- [Cursor](https://cursor.sh/) (build software faster in an editor designed for pair-programming with AI)
Note that unlike instruction-following text models (e.g., `text-davinci-002`), Codex is *not* trained to follow instructions. As a result, designing good prompts can take more care.
GPT-4 is more advanced than previous models like `gpt-3.5-turbo-instruct`. But, to get the best out of GPT-4 for coding tasks, it's still important to give clear and specific instructions. As a result, designing good prompts can take more care.
### More prompt advice
@ -136,17 +154,15 @@ For more prompt examples, visit [OpenAI Examples][OpenAI Examples].
In general, the input prompt is the best lever for improving model outputs. You can try tricks like:
* **Give more explicit instructions.** E.g., if you want the output to be a comma separated list, ask it to return a comma separated list. If you want it to say "I don't know" when it doesn't know the answer, tell it 'Say "I don't know" if you do not know the answer.'
* **Supply better examples.** If you're demonstrating examples in your prompt, make sure that your examples are diverse and high quality.
* **Ask the model to answer as if it was an expert.** Explicitly asking the model to produce high quality output or output as if it was written by an expert can induce the model to give higher quality answers that it thinks an expert would write. E.g., "The following answer is correct, high-quality, and written by an expert."
* **Prompt the model to write down the series of steps explaining its reasoning.** E.g., prepend your answer with something like "[Let's think step by step](https://arxiv.org/pdf/2205.11916v1.pdf)." Prompting the model to give an explanation of its reasoning before its final answer can increase the likelihood that its final answer is consistent and correct.
- **Be more specific** E.g., if you want the output to be a comma separated list, ask it to return a comma separated list. If you want it to say "I don't know" when it doesn't know the answer, tell it 'Say "I don't know" if you do not know the answer.' The more specific your instructions, the better the model can respond.
- **Provide Context**: Help the model understand the bigger picture of your request. This could be background information, examples/demonstrations of what you want or explaining the purpose of your task.
- **Ask the model to answer as if it was an expert.** Explicitly asking the model to produce high quality output or output as if it was written by an expert can induce the model to give higher quality answers that it thinks an expert would write. Phrases like "Explain in detail" or "Describe step-by-step" can be effective.
- **Prompt the model to write down the series of steps explaining its reasoning.** If understanding the 'why' behind an answer is important, prompt the model to include its reasoning. This can be done by simply adding a line like "[Let's think step by step](https://arxiv.org/abs/2205.11916)" before each answer.
[Fine Tuning Docs]: https://beta.openai.com/docs/guides/fine-tuning
[Codex Apps Blog Post]: https://openai.com/blog/codex-apps/
[Large language models Blog Post]: https://openai.com/blog/better-language-models/
[GitHub Copilot]: https://copilot.github.com/
[Codex]: https://openai.com/blog/openai-codex/
[Fine Tuning Docs]: https://platform.openai.com/docs/guides/fine-tuning
[OpenAI Customer Stories]: https://openai.com/customer-stories
[Large language models Blog Post]: https://openai.com/research/better-language-models
[GitHub Copilot]: https://github.com/features/copilot/
[GPT-4 and GPT-4 Turbo]: https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo
[GPT3 Apps Blog Post]: https://openai.com/blog/gpt-3-apps/
[OpenAI Examples]: https://beta.openai.com/examples
[OpenAI Examples]: https://platform.openai.com/examples

@ -11,6 +11,7 @@ People are writing great tools and papers for improving outputs from GPT. Here a
- [Guardrails.ai](https://shreyar.github.io/guardrails/): A Python library for validating outputs and retrying failures. Still in alpha, so expect sharp edges and bugs.
- [Guidance](https://github.com/microsoft/guidance): A handy looking Python library from Microsoft that uses Handlebars templating to interleave generation, prompting, and logical control.
- [Haystack](https://github.com/deepset-ai/haystack): Open-source LLM orchestration framework to build customizable, production-ready LLM applications in Python.
- [HoneyHive](https://honeyhive.ai): An enterprise platform to evaluate, debug, and monitor LLM apps.
- [LangChain](https://github.com/hwchase17/langchain): A popular Python/JavaScript library for chaining sequences of language model prompts.
- [LiteLLM](https://github.com/BerriAI/litellm): A minimal Python library for calling LLM APIs with a consistent format.
- [LlamaIndex](https://github.com/jerryjliu/llama_index): A Python library for augmenting LLM apps with data.
@ -34,6 +35,7 @@ People are writing great tools and papers for improving outputs from GPT. Here a
- [Lil'Log Prompt Engineering](https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/): An OpenAI researcher's review of the prompt engineering literature (as of March 2023).
- [OpenAI Cookbook: Techniques to improve reliability](https://cookbook.openai.com/articles/techniques_to_improve_reliability): A slightly dated (Sep 2022) review of techniques for prompting language models.
- [promptingguide.ai](https://www.promptingguide.ai/): A prompt engineering guide that demonstrates many techniques.
- [Xavi Amatriain's Prompt Engineering 101 Introduction to Prompt Engineering](https://amatriain.net/blog/PromptEngineering) and [202 Advanced Prompt Engineering](https://amatriain.net/blog/prompt201): A basic but opinionated introduction to prompt engineering and a follow up collection with many advanced methods starting with CoT.
## Video courses
@ -41,6 +43,7 @@ People are writing great tools and papers for improving outputs from GPT. Here a
- [Andrej Karpathy's Let's build GPT](https://www.youtube.com/watch?v=kCc8FmEb1nY): A detailed dive into the machine learning underlying GPT.
- [Prompt Engineering by DAIR.AI](https://www.youtube.com/watch?v=dOxUroR57xs): A one-hour video on various prompt engineering techniques.
- [Scrimba course about Assistants API](https://scrimba.com/learn/openaiassistants): A 30-minute interactive course about the Assistants API.
- [LinkedIn course: Introduction to Prompt Engineering: How to talk to the AIs](https://www.linkedin.com/learning/prompt-engineering-how-to-talk-to-the-ais/talking-to-the-ais?u=0): Short video introduction to prompt engineering
## Papers on advanced prompting to improve reasoning

@ -14,25 +14,25 @@ If you were asked to multiply 13 by 17, would the answer pop immediately into yo
Similarly, if you give GPT-3 a task that's too complex to do in the time it takes to calculate its next token, it may confabulate an incorrect guess. Yet, akin to humans, that doesn't necessarily mean the model is incapable of the task. With some time and space to reason things out, the model still may be able to answer reliably.
As an example, if you ask `text-davinci-002` the following math problem about juggling balls, it answers incorrectly:
As an example, if you ask `gpt-3.5-turbo-instruct` the following math problem about juggling balls, it answers incorrectly:
```text-davinci-002
```gpt-3.5-turbo-instruct
Q: A juggler has 16 balls. Half of the balls are golf balls and half of the golf balls are blue. How many blue golf balls are there?
A:
```
```text-davinci-002
```gpt-3.5-turbo-instruct
There are 8 blue golf balls.
```
Does this mean that GPT-3 cannot do simple math problems? No; in fact, it turns out that by prompting the model with `Let's think step by step`, the model solves the problem reliably:
```text-davinci-002
```gpt-3.5-turbo-instruct
Q: A juggler has 16 balls. Half of the balls are golf balls and half of the golf balls are blue. How many blue golf balls are there?
A: Let's think step by step.
```
```text-davinci-002
```gpt-3.5-turbo-instruct
There are 16 balls in total.
Half of the balls are golf balls.
That means that there are 8 golf balls.
@ -64,9 +64,9 @@ The rest of this article shares techniques for improving reliability of large la
One way to give a model more time and space to think is to break tasks into simpler pieces.
As an example, consider a task where we ask the model a multiple-choice question about some text - in this case, a game of Clue. When asked directly, `text-davinci-002` isn't able to put clues 3 & 5 together, and answers incorrectly:
As an example, consider a task where we ask the model a multiple-choice question about some text - in this case, a game of Clue. When asked directly, `gpt-3.5-turbo-instruct` isn't able to put clues 3 & 5 together, and answers incorrectly:
```text-davinci-002
```gpt-3.5-turbo-instruct
Use the following clues to answer the following multiple-choice question.
Clues:
@ -84,7 +84,7 @@ Question: Was Colonel Mustard in the observatory with the candlestick?
Solution:
```
```text-davinci-002
```gpt-3.5-turbo-instruct
(c) Unknown; there is not enough information to determine whether Colonel Mustard was in the observatory with the candlestick
```
@ -96,7 +96,7 @@ However, instead of asking for the answer directly, we can split the task into t
- Second, combine the relevant clues to reason out the answer to the question
- Third, write the final answer: either (a), (b), or (c)
```text-davinci-002
```gpt-3.5-turbo-instruct
Use the following clues to answer the following multiple-choice question, using the following procedure:
(1) First, go through the clues one by one and consider whether the clue is potentially relevant
(2) Second, combine the relevant clues to reason out the answer to the question
@ -118,7 +118,7 @@ Solution:
(1) First, go through the clues one by one and consider whether the clue is potentially relevant:
```
```text-davinci-002
```gpt-3.5-turbo-instruct
Clue 1: Miss Scarlett was the only person in the lounge. This clue is not relevant.
Clue 2: The person with the pipe was in the kitchen. This clue is not relevant.
Clue 3: Colonel Mustard was the only person in the observatory. This clue is relevant.
@ -136,9 +136,9 @@ By giving the model more time and space to think, and guiding it along a reasoni
Another benefit of splitting complex instructions into smaller subtasks is that it can help keep the model focused on each subtask.
For example, if we ask `text-davinci-002` to summarize a text in its original language, the model can lapse back into English:
For example, if we ask `gpt-3.5-turbo-instruct` to summarize a text in its original language, the model can lapse back into English:
```text-davinci-002
```gpt-3.5-turbo-instruct
Summarize the text using the original language of the text. The summary should be one sentence long.
Text:
@ -149,13 +149,13 @@ La estadística (la forma femenina del término alemán Statistik, derivado a su
Summary:
```
```text-davinci-002
```gpt-3.5-turbo-instruct
The text explains that statistics is a science that studies the variability, collection, organization, analysis, interpretation, and presentation of data, as well as the random process that generates them following the laws of probability.
```
However, if we first ask the model to identify the language of the text, and then summarize the text, it becomes more reliable:
```text-davinci-002
```gpt-3.5-turbo-instruct
First, identify the language of the text. Second, summarize the text using the original language of the text. The summary should be one sentence long.
Text:
@ -166,7 +166,7 @@ La estadística (la forma femenina del término alemán Statistik, derivado a su
Language:
```
```text-davinci-002
```gpt-3.5-turbo-instruct
Spanish
La estadística es una ciencia que estudia la variabilidad, colección, organización, análisis, interpretación, y presentación de los datos, así como el proceso aleatorio que los genera siguiendo las leyes de la probabilidad.
@ -203,7 +203,7 @@ To learn more, read the [full paper](https://arxiv.org/abs/2205.11916).
If you apply this technique to your own tasks, don't be afraid to experiment with customizing the instruction. `Let's think step by step` is rather generic, so you may find better performance with instructions that hew to a stricter format customized to your use case. For example, you can try more structured variants like `First, think step by step about why X might be true. Second, think step by step about why Y might be true. Third, think step by step about whether X or Y makes more sense.`. And you can even give the model an example format to help keep it on track, e.g.:
```text-davinci-002
```gpt-3.5-turbo-instruct
Using the IRS guidance below, answer the following questions using this format:
(1) For each criterion, determine whether it is met by the vehicle purchase
- {Criterion} Let's think step by step. {explanation} {yes or no, or if the question does not apply then N/A}.
@ -229,7 +229,7 @@ Solution:
- Does the vehicle have at least four wheels? Let's think step by step.
```
```text-davinci-002
```gpt-3.5-turbo-instruct
The Toyota Prius Prime has four wheels, so the answer is yes.
- Does the vehicle weigh less than 14,000 pounds? Let's think step by step. The Toyota Prius Prime weighs less than 14,000 pounds, so the answer is yes.
- Does the vehicle draw energy from a battery with at least 4 kilowatt hours that may be recharged from an external source? Let's think step by step. The Toyota Prius Prime has a battery with at least 4 kilowatt hours that may be recharged from an external source, so the answer is yes.

@ -8,8 +8,8 @@ Embeddings can be used for semantic search, recommendations, cluster analysis, n
For more information, read OpenAI's blog post announcements:
* [Introducing Text and Code Embeddings (Jan 2022)](https://openai.com/blog/introducing-text-and-code-embeddings/)
* [New and Improved Embedding Model (Dec 2022)](https://openai.com/blog/new-and-improved-embedding-model/)
- [Introducing Text and Code Embeddings (Jan 2022)](https://openai.com/blog/introducing-text-and-code-embeddings/)
- [New and Improved Embedding Model (Dec 2022)](https://openai.com/blog/new-and-improved-embedding-model/)
For comparison with other embedding models, see [Massive Text Embedding Benchmark (MTEB) Leaderboard](https://huggingface.co/spaces/mteb/leaderboard)
@ -19,14 +19,14 @@ Embeddings can be used for search either by themselves or as a feature in a larg
The simplest way to use embeddings for search is as follows:
* Before the search (precompute):
* Split your text corpus into chunks smaller than the token limit (8,191 tokens for `text-embedding-ada-002`)
* Embed each chunk of text
* Store those embeddings in your own database or in a vector search provider like [Pinecone](https://www.pinecone.io), [Weaviate](https://weaviate.io) or [Qdrant](https://qdrant.tech)
* At the time of the search (live compute):
* Embed the search query
* Find the closest embeddings in your database
* Return the top results
- Before the search (precompute):
- Split your text corpus into chunks smaller than the token limit (8,191 tokens for `text-embedding-3-small`)
- Embed each chunk of text
- Store those embeddings in your own database or in a vector search provider like [Pinecone](https://www.pinecone.io), [Weaviate](https://weaviate.io) or [Qdrant](https://qdrant.tech)
- At the time of the search (live compute):
- Embed the search query
- Find the closest embeddings in your database
- Return the top results
An example of how to use embeddings for search is shown in [Semantic_text_search_using_embeddings.ipynb](../examples/Semantic_text_search_using_embeddings.ipynb).

@ -37,13 +37,33 @@ ibigio:
name: "Ilan Bigio"
website: "https://twitter.com/ilanbigio"
avatar: "https://pbs.twimg.com/profile_images/1688302223250378752/z-99TOMH_400x400.jpg"
jhills20:
name: "James Hills"
website: "https://twitter.com/jimmerhills"
website: "https://twitter.com/jamesmhills"
avatar: "https://pbs.twimg.com/profile_images/1722092156691902464/44FGj7VT_400x400.jpg"
colin-openai:
name: "Colin Jarvis"
website: "https://twitter.com/colintjarvis"
avatar: "https://pbs.twimg.com/profile_images/1647875178947387393/aUc7D9m-_400x400.jpg"
avatar: "https://pbs.twimg.com/profile_images/1727207339034345472/IM8v8tlC_400x400.jpg"
prakul:
name: "Prakul Agarwal"
website: "https://www.linkedin.com/in/prakulagarwal"
avatar: "https://media.licdn.com/dms/image/D5603AQEUug83qKgRBg/profile-displayphoto-shrink_800_800/0/1675384960197?e=1706140800&v=beta&t=qxkDbBr-Bk2ASpcwbR5JVPD6yS-vzmIwNHAa8ApyDq4"
gaborcselle:
name: "Gabor Cselle"
website: "https://www.linkedin.com/in/gaborcselle"
avatar: "https://avatars.githubusercontent.com/u/54096?s=96&v=4"
nghiauet:
name: "Nghia Pham"
website: "https://www.linkedin.com/in/deptraicucmanh/"
avatar: "https://media.licdn.com/dms/image/D5603AQFpoi0l1lRQ7g/profile-displayphoto-shrink_200_200/0/1680006955681?e=1707350400&v=beta&t=1J1DAVRGXL7LgFUBnIooI-fsDRkdzFzCDwwxMze9N8c"
ggrn:
name: "Greg Richardson"
website: "https://twitter.com/ggrdson"
avatar: "https://pbs.twimg.com/profile_images/1371549909820657664/ZG-HDNlI_400x400.jpg"

@ -191,8 +191,10 @@
],
"source": [
"from openai import OpenAI\n",
"import os\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n",
"\n",
"client = OpenAI()\n",
"\n",
"assistant = client.beta.assistants.create(\n",
" name=\"Math Tutor\",\n",
@ -586,7 +588,7 @@
"\n",
"MATH_ASSISTANT_ID = assistant.id # or a hard-coded ID like \"asst-...\"\n",
"\n",
"client = OpenAI()\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n",
"\n",
"def submit_message(assistant_id, thread, user_message):\n",
" client.beta.threads.messages.create(\n",

@ -1,189 +0,0 @@
from typing import List, Union
from smokey import Smokey
import openai
def get_candidates(
prompt: str,
stop: List[str],
temperature: float,
priming_prefix: str,
engine: str,
n: int = 5,
) -> List[str]:
"""
Generate N candidate completions based on the prompt, generated with a specific temperature.
:param prompt: The prompt to start the conversation with.
:param stop: A list of tokens that indicate the end of the generation.
:param temperature: The temperature of the generation.
:param priming_prefix: The prefix to use for the priming.
:param engine: The engine to use for the generation.
:param n: The number of completions to generate.
:return: A list of completions.
"""
response = openai.Completion.create(
engine=engine,
prompt=prompt,
temperature=temperature,
max_tokens=150,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
stop=stop,
n=n,
)
responses = [priming_prefix + choice.text for choice in response.choices]
return responses
def rindex(lst: List, value: str) -> int:
"""
Return the index of the last occurrence of a value in a list.
:param lst: The list to search in.
:param value: The value to search for.
:return: The index of the last occurrence of the value.
"""
try:
return len(lst) - lst[::-1].index(value) - 1
except ValueError:
raise ValueError(f"Answer start token `{value}` not found in the eval template")
def eval_candidate(
candidate_answer: str,
original_instruction: str,
eval_template: str,
answer_start_token: str,
engine: str,
) -> float:
"""
Evaluate a candidate answer by calculating the average log probability
of the original instruction, given the candidate answer with a specific
evaluation template, aimed at reconstructing the original instruction.
:param candidate_answer: The candidate answer to evaluate.
:param original_instruction: The original instruction.
:param eval_template: The template to use for the evaluation.
:param answer_start_token: The token to use to indicate the start of the answer.
:param engine: The engine to use for the evaluation.
:return: The evaluation of the candidate answer.
"""
response = openai.Completion.create(
engine=engine,
prompt=eval_template.format(candidate_answer, original_instruction),
temperature=0,
max_tokens=0,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
logprobs=1,
echo=True,
)
answer_start = rindex(
response["choices"][0]["logprobs"]["tokens"], answer_start_token
)
logprobs = response["choices"][0]["logprobs"]["token_logprobs"][answer_start + 1 :]
return sum(logprobs) / len(logprobs)
def backtranslation(
prompt_template: str,
additional_info: str,
instruction: str,
eval_template: str,
priming_prefix: str = "SELECT",
stop1: List[str] = ["#", ";"],
answer_start_token: str = "--",
n: int = 5,
temperature: float = 0.5,
return_all_results: bool = False,
engine: str = "davinci-codex",
) -> Union[str, List[str, float]]:
"""
Generate a number of SQL queries given a natural language instruction,
and pick the best one based on the average log probability of explaining the
candidate SQL query with the exact original instruction, when prompted for
a natural language explanation of the candidate SQL query.
:param prompt_template: The template to use for the prompt to generate SQL.
:param additional_info: Additional information to include in the prompt
(SQL Tables, and their properties).
:param instruction: The instruction in natural language.
:param eval_template: The template to use for the evaluation.
:param priming_prefix: The prefix to use for the priming of the SQL query.
:param stop1: A list of tokens that indicate the end of the generation.
:param answer_start_token: The token to use to indicate the start of the
natural answer.
:param n: The number of candidates to generate.
:param temperature: The temperature of the generation.
:param return_all_results: Whether to return all results or just the best one.
:param engine: The engine to use for the generation and evaluation.
:return: The best SQL query, or a list of all scored generated SQL queries.
"""
prompt_template = prompt_template.format(
additional_info, instruction, priming_prefix
)
candidates = []
responses = get_candidates(
prompt_template, stop1, temperature, priming_prefix, engine=engine, n=n
)
for i in range(n):
quality = eval_candidate(
responses[i],
instruction,
eval_template,
answer_start_token,
engine=engine,
)
candidates.append((responses[i], quality))
candidates.sort(key=lambda x: x[1], reverse=True)
if return_all_results:
return candidates
return candidates[0][0]
def main(
nl_query: str = "Return the name of each department that had more than 10 employees in June 2021",
eval_template: str = "{};\n-- Explanation of the above query in human readable format\n-- {}",
table_definitions: str = "# Employee(id, name, department_id)\n# Department(id, name, address)\n# Salary_Payments(id, employee_id, amount, date)\n",
prompt_template: str = "### Postgres SQL tables, with their properties:\n#\n{}#\n### {}\n{}",
n: int = 3,
temperature: float = 0.3,
engine: str = "davinci-codex",
):
"""
Generate a number of SQL queries given a natural language instruction,
and pick the best one based on the highest backtranslation score.
:param nl_query: The natural language query.
:param eval_template: The template to use for the evaluation.
:param table_definitions: The definitions of the tables used in the query.
:param prompt_template: The template to use for the prompt to generate SQL.
:param n: The number of candidates to generate.
:param temperature: The temperature of the generation.
:param engine: The engine to use for the generation and evaluation.
:return: The best SQL query, or a list of all scored generated SQL queries.
"""
result = backtranslation(
prompt_template,
table_definitions,
nl_query,
eval_template,
priming_prefix="SELECT",
temperature=temperature,
n=n,
engine=engine,
)
print(result)
if __name__ == "__main__":
Smokey(main)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -22,7 +22,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@ -110,15 +110,15 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Total number of .py files: 57\n",
"Total number of functions extracted: 118\n"
"Total number of .py files: 51\n",
"Total number of functions extracted: 97\n"
]
}
],
@ -138,12 +138,12 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Now that we have our content, we can pass the data to the text-embedding-ada-002 endpoint to get back our vector embeddings."
"Now that we have our content, we can pass the data to the `text-embedding-3-small` model and get back our vector embeddings."
]
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 3,
"metadata": {},
"outputs": [
{
@ -179,35 +179,35 @@
" <td>def _console_log_level():\\n if openai.log i...</td>\n",
" <td>_console_log_level</td>\n",
" <td>openai/util.py</td>\n",
" <td>[0.033906757831573486, -0.00418944051489234, 0...</td>\n",
" <td>[0.005937571171671152, 0.05450401455163956, 0....</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>def log_debug(message, **params):\\n msg = l...</td>\n",
" <td>log_debug</td>\n",
" <td>openai/util.py</td>\n",
" <td>[-0.004059609025716782, 0.004895503632724285, ...</td>\n",
" <td>[0.017557814717292786, 0.05647840350866318, -0...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>def log_info(message, **params):\\n msg = lo...</td>\n",
" <td>log_info</td>\n",
" <td>openai/util.py</td>\n",
" <td>[0.0048639848828315735, 0.0033139237202703953,...</td>\n",
" <td>[0.022524144500494003, 0.06219055876135826, -0...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>def log_warn(message, **params):\\n msg = lo...</td>\n",
" <td>log_warn</td>\n",
" <td>openai/util.py</td>\n",
" <td>[0.0024026145692914724, -0.010721310041844845,...</td>\n",
" <td>[0.030524108558893204, 0.0667714849114418, -0....</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>def logfmt(props):\\n def fmt(key, val):\\n ...</td>\n",
" <td>logfmt</td>\n",
" <td>openai/util.py</td>\n",
" <td>[0.01664826273918152, 0.01730910874903202, 0.0...</td>\n",
" <td>[0.05337328091263771, 0.03697286546230316, -0....</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
@ -222,14 +222,14 @@
"4 def logfmt(props):\\n def fmt(key, val):\\n ... logfmt \n",
"\n",
" filepath code_embedding \n",
"0 openai/util.py [0.033906757831573486, -0.00418944051489234, 0... \n",
"1 openai/util.py [-0.004059609025716782, 0.004895503632724285, ... \n",
"2 openai/util.py [0.0048639848828315735, 0.0033139237202703953,... \n",
"3 openai/util.py [0.0024026145692914724, -0.010721310041844845,... \n",
"4 openai/util.py [0.01664826273918152, 0.01730910874903202, 0.0... "
"0 openai/util.py [0.005937571171671152, 0.05450401455163956, 0.... \n",
"1 openai/util.py [0.017557814717292786, 0.05647840350866318, -0... \n",
"2 openai/util.py [0.022524144500494003, 0.06219055876135826, -0... \n",
"3 openai/util.py [0.030524108558893204, 0.0667714849114418, -0.... \n",
"4 openai/util.py [0.05337328091263771, 0.03697286546230316, -0.... "
]
},
"execution_count": 11,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
@ -238,7 +238,7 @@
"from utils.embeddings_utils import get_embedding\n",
"\n",
"df = pd.DataFrame(all_funcs)\n",
"df['code_embedding'] = df['code'].apply(lambda x: get_embedding(x, model='text-embedding-ada-002'))\n",
"df['code_embedding'] = df['code'].apply(lambda x: get_embedding(x, model='text-embedding-3-small'))\n",
"df['filepath'] = df['filepath'].map(lambda x: Path(x).relative_to(code_root))\n",
"df.to_csv(\"data/code_search_openai-python.csv\", index=False)\n",
"df.head()"
@ -255,21 +255,21 @@
"\n",
"We define a search_functions method that takes our data that contains our embeddings, a query string, and some other configuration options. The process of searching our database works like such:\n",
"\n",
"1. We first embed our query string (code_query) with text-embedding-ada-002. The reasoning here is that a query string like 'a function that reverses a string' and a function like 'def reverse(string): return string[::-1]' will be very similar when embedded.\n",
"1. We first embed our query string (code_query) with `text-embedding-3-small`. The reasoning here is that a query string like 'a function that reverses a string' and a function like 'def reverse(string): return string[::-1]' will be very similar when embedded.\n",
"2. We then calculate the cosine similarity between our query string embedding and all data points in our database. This gives a distance between each point and our query.\n",
"3. We finally sort all of our data points by their distance to our query string and return the number of results requested in the function parameters. "
]
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"from utils.embeddings_utils import cosine_similarity\n",
"\n",
"def search_functions(df, code_query, n=3, pprint=True, n_lines=7):\n",
" embedding = get_embedding(code_query, model='text-embedding-ada-002')\n",
" embedding = get_embedding(code_query, model='text-embedding-3-small')\n",
" df['similarities'] = df.code_embedding.apply(lambda x: cosine_similarity(x, embedding))\n",
"\n",
" res = df.sort_values('similarities', ascending=False).head(n)\n",
@ -285,14 +285,14 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"openai/validators.py:format_inferrer_validator score=0.751\n",
"openai/validators.py:format_inferrer_validator score=0.453\n",
"def format_inferrer_validator(df):\n",
" \"\"\"\n",
" This validator will infer the likely fine-tuning format of the data, and display it to the user if it is classification.\n",
@ -301,16 +301,7 @@
" ft_type = infer_task_type(df)\n",
" immediate_msg = None\n",
"----------------------------------------------------------------------\n",
"openai/validators.py:get_validators score=0.748\n",
"def get_validators():\n",
" return [\n",
" num_examples_validator,\n",
" lambda x: necessary_column_validator(x, \"prompt\"),\n",
" lambda x: necessary_column_validator(x, \"completion\"),\n",
" additional_column_validator,\n",
" non_empty_field_validator,\n",
"----------------------------------------------------------------------\n",
"openai/validators.py:infer_task_type score=0.739\n",
"openai/validators.py:infer_task_type score=0.37\n",
"def infer_task_type(df):\n",
" \"\"\"\n",
" Infer the likely fine-tuning task type from the data\n",
@ -318,6 +309,15 @@
" CLASSIFICATION_THRESHOLD = 3 # min_average instances of each class\n",
" if sum(df.prompt.str.len()) == 0:\n",
" return \"open-ended generation\"\n",
"----------------------------------------------------------------------\n",
"openai/validators.py:apply_validators score=0.369\n",
"def apply_validators(\n",
" df,\n",
" fname,\n",
" remediation,\n",
" validators,\n",
" auto_accept,\n",
" write_out_file_func,\n",
"----------------------------------------------------------------------\n"
]
}
@ -328,14 +328,14 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"openai/validators.py:get_common_xfix score=0.794\n",
"openai/validators.py:get_common_xfix score=0.487\n",
"def get_common_xfix(series, xfix=\"suffix\"):\n",
" \"\"\"\n",
" Finds the longest common suffix or prefix of all the values in a series\n",
@ -347,7 +347,7 @@
" if xfix == \"suffix\"\n",
" else series.str[: len(common_xfix) + 1]\n",
"----------------------------------------------------------------------\n",
"openai/validators.py:common_completion_suffix_validator score=0.778\n",
"openai/validators.py:common_completion_suffix_validator score=0.449\n",
"def common_completion_suffix_validator(df):\n",
" \"\"\"\n",
" 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",
@ -368,14 +368,14 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"openai/cli.py:tools_register score=0.78\n",
"openai/cli.py:tools_register score=0.391\n",
"def tools_register(parser):\n",
" subparsers = parser.add_subparsers(\n",
" title=\"Tools\", help=\"Convenience client side tools\"\n",
@ -421,7 +421,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.9"
"version": "3.11.5"
},
"orig_nbformat": 4
},

@ -0,0 +1,993 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Creating slides with the Assistants API (GPT-4), and DALL·E-3\n",
"\n",
"This notebook illustrates the use of the new [Assistants API](https://platform.openai.com/docs/assistants/overview) (GPT-4), and DALL·E-3 in crafting informative and visually appealing slides. <br>\n",
"Creating slides is a pivotal aspect of many jobs, but can be laborious and time-consuming. Additionally, extracting insights from data and articulating them effectively on slides can be challenging. <br><br> This cookbook recipe will demonstrate how you can utilize the new Assistants API to faciliate the end to end slide creation process for you without you having to touch Microsoft PowerPoint or Google Slides, saving you valuable time and effort!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 0. Setup"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from IPython.display import display, Image\n",
"from openai import OpenAI\n",
"import os\n",
"import pandas as pd\n",
"import json\n",
"import io\n",
"from PIL import Image\n",
"import requests\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n",
"\n",
"#Lets import some helper functions for assistants from https://cookbook.openai.com/examples/assistants_api_overview_python\n",
"def show_json(obj):\n",
" display(json.loads(obj.model_dump_json()))\n",
"\n",
"def submit_message(assistant_id, thread, user_message,file_ids=None):\n",
" params = {\n",
" 'thread_id': thread.id,\n",
" 'role': 'user',\n",
" 'content': user_message,\n",
" }\n",
" if file_ids:\n",
" params['file_ids']=file_ids\n",
"\n",
" client.beta.threads.messages.create(\n",
" **params\n",
")\n",
" return client.beta.threads.runs.create(\n",
" thread_id=thread.id,\n",
" assistant_id=assistant_id,\n",
")\n",
"\n",
"def get_response(thread):\n",
" return client.beta.threads.messages.list(thread_id=thread.id)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. Creating the content"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this recipe, we will be creating a brief fictional presentation for the quarterly financial review of our company, NotReal Corporation. We want to highlight some key trends we are seeing that are affecting the profitability of our company.<br> Let's say we have the some financial data at our disposal. Let's load in the data, and take a look..."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Year</th>\n",
" <th>Quarter</th>\n",
" <th>Distribution channel</th>\n",
" <th>Revenue ($M)</th>\n",
" <th>Costs ($M)</th>\n",
" <th>Customer count</th>\n",
" <th>Time</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2021</td>\n",
" <td>Q1</td>\n",
" <td>Online Sales</td>\n",
" <td>1.50</td>\n",
" <td>1.301953</td>\n",
" <td>150</td>\n",
" <td>2021 Q1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2021</td>\n",
" <td>Q1</td>\n",
" <td>Direct Sales</td>\n",
" <td>1.50</td>\n",
" <td>1.380809</td>\n",
" <td>151</td>\n",
" <td>2021 Q1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2021</td>\n",
" <td>Q1</td>\n",
" <td>Retail Partners</td>\n",
" <td>1.50</td>\n",
" <td>1.348246</td>\n",
" <td>152</td>\n",
" <td>2021 Q1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2021</td>\n",
" <td>Q2</td>\n",
" <td>Online Sales</td>\n",
" <td>1.52</td>\n",
" <td>1.308608</td>\n",
" <td>152</td>\n",
" <td>2021 Q2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2021</td>\n",
" <td>Q2</td>\n",
" <td>Direct Sales</td>\n",
" <td>1.52</td>\n",
" <td>1.413305</td>\n",
" <td>153</td>\n",
" <td>2021 Q2</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Year Quarter Distribution channel Revenue ($M) Costs ($M) \\\n",
"0 2021 Q1 Online Sales 1.50 1.301953 \n",
"1 2021 Q1 Direct Sales 1.50 1.380809 \n",
"2 2021 Q1 Retail Partners 1.50 1.348246 \n",
"3 2021 Q2 Online Sales 1.52 1.308608 \n",
"4 2021 Q2 Direct Sales 1.52 1.413305 \n",
"\n",
" Customer count Time \n",
"0 150 2021 Q1 \n",
"1 151 2021 Q1 \n",
"2 152 2021 Q1 \n",
"3 152 2021 Q2 \n",
"4 153 2021 Q2 "
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"financial_data_path = 'data/NotRealCorp_financial_data.json'\n",
"financial_data = pd.read_json(financial_data_path)\n",
"financial_data.head(5)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As you can see, this data has quarterly revenue, costs and customer data across different distribution channels. Let's create an Assistant\n",
"that can act as a personal analyst and make a nice visualization for our PowerPoint!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First, we need to upload our file so our Assistant can access it."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"file = client.files.create(\n",
" file=open('data/NotRealCorp_financial_data.json',\"rb\"),\n",
" purpose='assistants',\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we're ready to create our Assistant. We can instruct our assistant to act as a data scientist, and take any queries we give it and run the necessary code to output the proper data visualization. The instructions parameter here is akin to system instructions in the ChatCompletions endpoint, and can help guide the assistant. We can also turn on the tool of Code Interpreter, so our Assistant will be able to code. Finally, we can specifiy any files we want to use, which in this case is just the `financial_data` file we created above."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"assistant = client.beta.assistants.create(\n",
" instructions=\"You are a data scientist assistant. When given data and a query, write the proper code and create the proper visualization\",\n",
" model=\"gpt-4-1106-preview\",\n",
" tools=[{\"type\": \"code_interpreter\"}],\n",
" file_ids=[file.id]\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's create a thread now, and as our first request ask the Assistant to calculate quarterly profits, and then plot the profits by distribution channel over time. The assistant will automatically calculate the profit for each quarter, and also create a new column combining quarter and year, without us having to ask for that directly. We can also specify the colors of each line."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"thread = client.beta.threads.create(\n",
" messages=[\n",
" {\n",
" \"role\": \"user\",\n",
" \"content\": \"Calculate profit (revenue minus cost) by quarter and year, and visualize as a line plot across the distribution channels, where the colors of the lines are green, light red, and light blue\",\n",
" \"file_ids\": [file.id]\n",
" }\n",
" ]\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"No we can execute the run of our thread"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"\n",
"run = client.beta.threads.runs.create(\n",
" thread_id=thread.id,\n",
" assistant_id=assistant.id,\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can now start a loop that will check if the image has been created. Note: This may take a few minutes"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"messages = client.beta.threads.messages.list(thread_id=thread.id)\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Assistant still working...\n",
"Plot created!\n"
]
}
],
"source": [
"import time\n",
"\n",
"while True:\n",
" messages = client.beta.threads.messages.list(thread_id=thread.id)\n",
" try:\n",
" #See if image has been created\n",
" messages.data[0].content[0].image_file\n",
" #Sleep to make sure run has completed\n",
" time.sleep(5)\n",
" print('Plot created!')\n",
" break\n",
" except:\n",
" time.sleep(10)\n",
" print('Assistant still working...')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's see the messages the Assistant added."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[MessageContentImageFile(image_file=ImageFile(file_id='file-0rKABLygI02MgwwhpgWdRFY1'), type='image_file'),\n",
" MessageContentText(text=Text(annotations=[], value=\"The profit has been calculated for each distribution channel by quarter and year. Next, I'll create a line plot to visualize these profits. As specified, I will use green for the 'Online Sales', light red for 'Direct Sales', and light blue for 'Retail Partners' channels. Let's create the plot.\"), type='text'),\n",
" MessageContentText(text=Text(annotations=[], value=\"The JSON data has been successfully restructured into a tabular dataframe format. It includes the year, quarter, distribution channel, revenue, costs, customer count, and a combined 'Time' representation of 'Year Quarter'. Now, we have the necessary information to calculate the profit (revenue minus cost) by quarter and year.\\n\\nTo visualize the profit across the different distribution channels with a line plot, we will proceed with the following steps:\\n\\n1. Calculate the profit for each row in the dataframe.\\n2. Group the data by 'Time' (which is a combination of Year and Quarter) and 'Distribution channel'.\\n3. Aggregate the profit for each group.\\n4. Plot the aggregated profits as a line plot with the distribution channels represented in different colors as requested.\\n\\nLet's calculate the profit for each row and then continue with the visualization.\"), type='text'),\n",
" MessageContentText(text=Text(annotations=[], value='The structure of the JSON data shows that it is a dictionary with \"Year\", \"Quarter\", \"Distribution channel\", and potentially other keys that map to dictionaries containing the data. The keys of the inner dictionaries are indices, indicating that the data is tabular but has been converted into a JSON object.\\n\\nTo properly convert this data into a DataFrame, I will restructure the JSON data into a more typical list of dictionaries, where each dictionary represents a row in our target DataFrame. Subsequent to this restructuring, I can then load the data into a Pandas DataFrame. Let\\'s restructure and load the data.'), type='text'),\n",
" MessageContentText(text=Text(annotations=[], value=\"The JSON data has been incorrectly loaded into a single-row DataFrame with numerous columns representing each data point. This implies the JSON structure is not as straightforward as expected, and a direct conversion to a flat table is not possible without further processing.\\n\\nTo better understand the JSON structure and figure out how to properly normalize it into a table format, I will print out the raw JSON data structure. We will analyze its format and then determine the correct approach to extract the profit by quarter and year, as well as the distribution channel information. Let's take a look at the JSON structure.\"), type='text'),\n",
" MessageContentText(text=Text(annotations=[], value=\"It seems that the file content was successfully parsed as JSON, and thus, there was no exception raised. The variable `error_message` is not defined because the `except` block was not executed.\\n\\nI'll proceed with displaying the data that was parsed from JSON.\"), type='text'),\n",
" MessageContentText(text=Text(annotations=[], value=\"It appears that the content of the dataframe has been incorrectly parsed, resulting in an empty dataframe with a very long column name that seems to contain JSON data rather than typical CSV columns and rows.\\n\\nTo address this issue, I will take a different approach to reading the file. I will attempt to parse the content as JSON. If this is not successful, I'll adjust the loading strategy accordingly. Let's try to read the contents as JSON data first.\"), type='text'),\n",
" MessageContentText(text=Text(annotations=[], value=\"Before we can calculate profits and visualize the data as requested, I need to first examine the contents of the file that you have uploaded. Let's go ahead and read the file to understand its structure and the kind of data it contains. Once I have a clearer picture of the dataset, we can proceed with the profit calculations. I'll begin by loading the file into a dataframe and displaying the first few entries to see the data schema.\"), type='text'),\n",
" MessageContentText(text=Text(annotations=[], value='Calculate profit (revenue minus cost) by quarter and year, and visualize as a line plot across the distribution channels, where the colors of the lines are green, light red, and light blue'), type='text')]"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"messages = client.beta.threads.messages.list(thread_id=thread.id)\n",
"[message.content[0] for message in messages.data]\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can see that the last message (latest message is shown first) from the assistant contains the image file we are looking for. An interesting note here is that the Assistant was able to attempt several times to parse the JSON data, as the first parsing was unsuccessful, demonstrating the assistant's adaptability."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"# Quick helper function to convert our output file to a png\n",
"def convert_file_to_png(file_id, write_path):\n",
" data = client.files.content(file_id)\n",
" data_bytes = data.read()\n",
" with open(write_path, \"wb\") as file:\n",
" file.write(data_bytes)\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"plot_file_id = messages.data[0].content[0].image_file.file_id\n",
"image_path = \"../images/NotRealCorp_chart.png\"\n",
"convert_file_to_png(plot_file_id,image_path)\n",
"\n",
"#Upload\n",
"plot_file = client.files.create(\n",
" file=open(image_path, \"rb\"),\n",
" purpose='assistants'\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's load in the plot!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![The Image](../images/NotRealCorp_chart.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Nice! So, with just one sentence, we were able to have our assistant use code interpreter to\n",
"calculate the profitability, and graph the three lineplots of the various distribution channels.<br><br>\n",
"Now we have a nice visual for our slide, but we want some insights to go along with it."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. Generating insights"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To get insights from our image, we simply need to add a new message to our thread. Our Assistant will know to use the message history to give us some concise takeaways from the visual provided. "
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Run(id='run_NWoygMcBfHUr58fCE4Cn6rxN', assistant_id='asst_3T362kLlTyAq0FUnkvjjQczO', cancelled_at=None, completed_at=None, created_at=1701827074, expires_at=1701827674, failed_at=None, file_ids=['file-piTokyHGllwGITzIpoG8dok3'], instructions='You are a data scientist assistant. When given data and a query, write the proper code and create the proper visualization', last_error=None, metadata={}, model='gpt-4-1106-preview', object='thread.run', required_action=None, started_at=None, status='queued', thread_id='thread_73TgtFoJMlEJvb13ngjTnAo3', tools=[ToolAssistantToolsCode(type='code_interpreter')])"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"submit_message(assistant.id,thread,\"Give me two medium length sentences (~20-30 words per sentence) of the \\\n",
" most important insights from the plot you just created.\\\n",
" These will be used for a slide deck, and they should be about the\\\n",
" 'so what' behind the data.\"\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, once the run has completed, we can see the latest message"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The plot reveals a consistent upward trend in profits for all distribution channels, indicating successful business growth over time. Particularly, 'Online Sales' shows a notable increase, underscoring the importance of digital presence in revenue generation.\n"
]
}
],
"source": [
"# Hard coded wait for a response, as the assistant may iterate on the bullets.\n",
"time.sleep(10)\n",
"response = get_response(thread)\n",
"bullet_points = response.data[0].content[0].text.value\n",
"print(bullet_points)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cool! So our assistant was able to identify the noteworthy growth in Online Sales profit, and infer that this shows the importance of a large digital presence. Now let's get a compelling title for the slide."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Run(id='run_q6E85J31jCw3QkHpjJKl969P', assistant_id='asst_3T362kLlTyAq0FUnkvjjQczO', cancelled_at=None, completed_at=None, created_at=1701827084, expires_at=1701827684, failed_at=None, file_ids=['file-piTokyHGllwGITzIpoG8dok3'], instructions='You are a data scientist assistant. When given data and a query, write the proper code and create the proper visualization', last_error=None, metadata={}, model='gpt-4-1106-preview', object='thread.run', required_action=None, started_at=None, status='queued', thread_id='thread_73TgtFoJMlEJvb13ngjTnAo3', tools=[ToolAssistantToolsCode(type='code_interpreter')])"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"submit_message(assistant.id,thread,\"Given the plot and bullet points you created,\\\n",
" come up with a very brief title for a slide. It should reflect just the main insights you came up with.\"\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And the title is:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\"Ascending Profits & Digital Dominance\"\n"
]
}
],
"source": [
"#Wait as assistant may take a few steps\n",
"time.sleep(10)\n",
"response = get_response(thread)\n",
"title = response.data[0].content[0].text.value\n",
"print(title)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. DALL·E-3 title image"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Nice, now we have a title, a plot and two bullet points. We're almost ready to put this all on a slide, but as a final step, let's have DALL·E-3 come up with an image to use as the title slide of the presentation. <br><br>\n",
"*Note:* DALL·E-3 is not yet available within the assistants API but is coming soon! <br> <br>\n",
"We'll feed in a brief description of our company (NotRealCorp) and have DALL·E-3 do the rest!"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"company_summary = \"NotReal Corp is a prominent hardware company that manufactures and sells processors, graphics cards and other essential computer hardware.\"\n"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"response = client.images.generate(\n",
" model='dall-e-3',\n",
" prompt=f\"given this company summary {company_summary}, create an inspirational \\\n",
" photo showing the growth and path forward. This will be used at a quarterly\\\n",
" financial planning meeting\",\n",
" size=\"1024x1024\",\n",
" quality=\"hd\",\n",
" n=1\n",
")\n",
"image_url = response.data[0].url\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cool, now we can add this image to our thread. First, we can save the image locally, then upload it to the assistants API using the `File` upload endpoint. Let's also take a look at our image"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"dalle_img_path = '../images/dalle_image.png'\n",
"img = requests.get(image_url)\n",
"\n",
"#Save locally\n",
"with open(dalle_img_path,'wb') as file:\n",
" file.write(img.content)\n",
"\n",
"#Upload\n",
"dalle_file = client.files.create(\n",
" file=open(dalle_img_path, \"rb\"),\n",
" purpose='assistants'\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" \n",
"![Image](../images/dalle_image.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. Creating the slides"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We now have all the content we need to create the slides. While we could simply add a message asking for slides, but let's instead give the assistant a slide template, using the `python-pptx` library, to use. This will ensure we get a deck in the style we want. See the `Extensions` section at the end of the notebook for notes on creating the template."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"title_template = \"\"\"\n",
"from pptx import Presentation\n",
"from pptx.util import Inches, Pt\n",
"from pptx.enum.text import PP_PARAGRAPH_ALIGNMENT\n",
"from pptx.dml.color import RGBColor\n",
"\n",
"# Create a new presentation object\n",
"prs = Presentation()\n",
"\n",
"# Add a blank slide layout\n",
"blank_slide_layout = prs.slide_layouts[6]\n",
"slide = prs.slides.add_slide(blank_slide_layout)\n",
"\n",
"# Set the background color of the slide to black\n",
"background = slide.background\n",
"fill = background.fill\n",
"fill.solid()\n",
"fill.fore_color.rgb = RGBColor(0, 0, 0)\n",
"\n",
"# Add image to the left side of the slide with a margin at the top and bottom\n",
"left = Inches(0)\n",
"top = Inches(0)\n",
"height = prs.slide_height\n",
"width = prs.slide_width * 3/5\n",
"pic = slide.shapes.add_picture(image_path, left, top, width=width, height=height)\n",
"\n",
"# Add title text box positioned higher\n",
"left = prs.slide_width * 3/5\n",
"top = Inches(2)\n",
"width = prs.slide_width * 2/5\n",
"height = Inches(1)\n",
"title_box = slide.shapes.add_textbox(left, top, width, height)\n",
"title_frame = title_box.text_frame\n",
"title_p = title_frame.add_paragraph()\n",
"title_p.text = title_text\n",
"title_p.font.bold = True\n",
"title_p.font.size = Pt(38)\n",
"title_p.font.color.rgb = RGBColor(255, 255, 255)\n",
"title_p.alignment = PP_PARAGRAPH_ALIGNMENT.CENTER\n",
"\n",
"# Add subtitle text box\n",
"left = prs.slide_width * 3/5\n",
"top = Inches(3)\n",
"width = prs.slide_width * 2/5\n",
"height = Inches(1)\n",
"subtitle_box = slide.shapes.add_textbox(left, top, width, height)\n",
"subtitle_frame = subtitle_box.text_frame\n",
"subtitle_p = subtitle_frame.add_paragraph()\n",
"subtitle_p.text = subtitle_text\n",
"subtitle_p.font.size = Pt(22)\n",
"subtitle_p.font.color.rgb = RGBColor(255, 255, 255)\n",
"subtitle_p.alignment = PP_PARAGRAPH_ALIGNMENT.CENTER\n",
"\"\"\"\n",
"\n",
"data_vis_template = \"\"\"\n",
"from pptx import Presentation\n",
"from pptx.util import Inches, Pt\n",
"from pptx.enum.text import PP_PARAGRAPH_ALIGNMENT\n",
"from pptx.dml.color import RGBColor\n",
"\n",
"# Create a new presentation object\n",
"prs = Presentation()\n",
"\n",
"# Add a blank slide layout\n",
"blank_slide_layout = prs.slide_layouts[6]\n",
"slide = prs.slides.add_slide(blank_slide_layout)\n",
"\n",
"# Set the background color of the slide to black\n",
"background = slide.background\n",
"fill = background.fill\n",
"fill.solid()\n",
"fill.fore_color.rgb = RGBColor(0, 0, 0)\n",
"\n",
"# Define placeholders\n",
"image_path = data_vis_img\n",
"title_text = \"Maximizing Profits: The Dominance of Online Sales & Direct Sales Optimization\"\n",
"bullet_points = \"• Online Sales consistently lead in profitability across quarters, indicating a strong digital market presence.\\n• Direct Sales show fluctuations, suggesting variable performance and the need for targeted improvements in that channel.\"\n",
"\n",
"# Add image placeholder on the left side of the slide\n",
"left = Inches(0.2)\n",
"top = Inches(1.8)\n",
"height = prs.slide_height - Inches(3)\n",
"width = prs.slide_width * 3/5\n",
"pic = slide.shapes.add_picture(image_path, left, top, width=width, height=height)\n",
"\n",
"# Add title text spanning the whole width\n",
"left = Inches(0)\n",
"top = Inches(0)\n",
"width = prs.slide_width\n",
"height = Inches(1)\n",
"title_box = slide.shapes.add_textbox(left, top, width, height)\n",
"title_frame = title_box.text_frame\n",
"title_frame.margin_top = Inches(0.1)\n",
"title_p = title_frame.add_paragraph()\n",
"title_p.text = title_text\n",
"title_p.font.bold = True\n",
"title_p.font.size = Pt(28)\n",
"title_p.font.color.rgb = RGBColor(255, 255, 255)\n",
"title_p.alignment = PP_PARAGRAPH_ALIGNMENT.CENTER\n",
"\n",
"# Add hardcoded \"Key Insights\" text and bullet points\n",
"left = prs.slide_width * 2/3\n",
"top = Inches(1.5)\n",
"width = prs.slide_width * 1/3\n",
"height = Inches(4.5)\n",
"insights_box = slide.shapes.add_textbox(left, top, width, height)\n",
"insights_frame = insights_box.text_frame\n",
"insights_p = insights_frame.add_paragraph()\n",
"insights_p.text = \"Key Insights:\"\n",
"insights_p.font.bold = True\n",
"insights_p.font.size = Pt(24)\n",
"insights_p.font.color.rgb = RGBColor(0, 128, 100)\n",
"insights_p.alignment = PP_PARAGRAPH_ALIGNMENT.LEFT\n",
"insights_frame.add_paragraph()\n",
"\n",
"\n",
"bullet_p = insights_frame.add_paragraph()\n",
"bullet_p.text = bullet_points\n",
"bullet_p.font.size = Pt(12)\n",
"bullet_p.font.color.rgb = RGBColor(255, 255, 255)\n",
"bullet_p.line_spacing = 1.5\n",
"\"\"\"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's set a few quick variables for our slides. We want the company name, NotRealCorp, to be on the title slide, and the title of the presentation should 'Quartlerly financial planning metting, Q3, 2023'."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"title_text = \"NotRealCorp\"\n",
"subtitle_text = \"Quarterly financial planning meeting, Q3 2023\"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And for the data slide, we have:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here we have a template to create a Title Slide. The template below was created by uploading the image of a desirable title slide to GPT-V, and asking for the `python-pptx` code to create that template. The inputs to the template are the image_path, title_text, and subtitle_text."
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Run(id='run_taLrnOnlDhoywgQFFBOLPlg0', assistant_id='asst_3T362kLlTyAq0FUnkvjjQczO', cancelled_at=None, completed_at=None, created_at=1701827118, expires_at=1701827718, failed_at=None, file_ids=['file-piTokyHGllwGITzIpoG8dok3'], instructions='You are a data scientist assistant. When given data and a query, write the proper code and create the proper visualization', last_error=None, metadata={}, model='gpt-4-1106-preview', object='thread.run', required_action=None, started_at=None, status='queued', thread_id='thread_73TgtFoJMlEJvb13ngjTnAo3', tools=[ToolAssistantToolsCode(type='code_interpreter')])"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"submit_message(assistant.id,thread,f\"Use the included code template to create a PPTX slide that follows the template format, but uses the image, company name/title, and document name/subtitle included:\\\n",
"{title_template}. IMPORTANT: Use the image file included in this message as the image_path image in this first slide, and use the Company Name {title_text} as the title_text variable, and \\\n",
" use the subtitle_text {subtitle_text} a the subtitle_text variable. \\\n",
" NEST, create a SECOND slide using the following code template: {data_vis_template} to create a PPTX slide that follows the template format, but uses the company name/title, and document name/subtitle included:\\\n",
"{data_vis_template}. IMPORTANT: Use the line plot image, that is the second attached image in this message, that you created earlier in the thread as the data_vis_img image, and use the data visualization title that you created earlier for the variable title_text, and\\\n",
" the bullet points of insights you created earlier for the bullet_points variable. Output these TWO SLIDES as a .pptx file. Make sure the output is two slides, with each slide matching the respective template given in this message.\",\n",
" file_ids=[dalle_file.id, plot_file.id]\n",
")\n"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Assistant still working on PPTX...\n",
"Assistant still working on PPTX...\n",
"Assistant still working on PPTX...\n",
"Assistant still working on PPTX...\n",
"Assistant still working on PPTX...\n",
"Assistant still working on PPTX...\n",
"Assistant still working on PPTX...\n",
"Assistant still working on PPTX...\n",
"Assistant still working on PPTX...\n",
"Assistant still working on PPTX...\n",
"Successfully retrieved pptx_id: file-oa0i63qPH4IaJXYj90aA6L4Q\n"
]
}
],
"source": [
"#May take 1-3 mins\n",
"while True:\n",
" try:\n",
" response = get_response(thread)\n",
" pptx_id = response.data[0].content[0].text.annotations[0].file_path.file_id\n",
" print(\"Successfully retrieved pptx_id:\", pptx_id)\n",
" break\n",
" except Exception as e:\n",
" print(\"Assistant still working on PPTX...\")\n",
" time.sleep(10)\n"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
"pptx_id = response.data[0].content[0].text.annotations[0].file_path.file_id\n",
"ppt_file= client.files.content(pptx_id)\n",
"file_obj = io.BytesIO(ppt_file.read())\n",
"with open(\"data/created_slides.pptx\", \"wb\") as f:\n",
" f.write(file_obj.getbuffer())\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we have a PPTX file saved with all of our created content!. <br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's look at the screenshots of the .pptx we just created using JUST the assistants API and DALL·E-3. We don't have a `seed` parameter yet in the Assistants API, so the DALL·E-3 image and wordings will be slightly different from what you see when you run this notebook, due to the non-determinism of LLMs, but the outputs should be directionally the same."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The title slide:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![Title Slide](../images/title_slide.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And the data slide:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![Data Slide](../images/data_vis_slide.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. Conclusion"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Woo! While these slides could use some formatting tweaks, we have made some great content using the Assistants API, GPT-4 and DALL·E-3. We were able to take a `.csv` file with financial data, and use our assisant to calculate profit by quarter across distribution channels, plot the results, identify insights and key takeaways from the visualization, and create a summarative title. And, given just a description of our company, NotRealCorp, we used DALL·E-3 to make an awesome title image. <br><br>\n",
"While we are still a ways away from entirely automating this process without a human in the loop, hopefully this notebook can make the slide creation process a bit easier for you. More importantly, this notebook can ideally give you a glimpse into the potential of the assistants API! We're excited to see what you build."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. Extensions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- When DALL·E-3 is incorporated in the Assistants API, we will have the ability to request the generated title image within the thread. \n",
"- GPT-4-Vision is not yet supported in the Assistants API, but could have been used to gather insights from the line plot image.\n",
"- GPT-4-Vision was used to generate the `python-pptx` template included in this recipe, so a potential extension project could be demonstrating best practices around converting images to slide templates."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "openai",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.16"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

@ -78,7 +78,7 @@
"source": [
"# input parameters\n",
"embedding_cache_path = \"data/snli_embedding_cache.pkl\" # embeddings will be saved/loaded here\n",
"default_embedding_engine = \"babbage-similarity\" # text-embedding-ada-002 is recommended\n",
"default_embedding_engine = \"text-embedding-3-small\"\n",
"num_pairs_to_embed = 1000 # 1000 is arbitrary\n",
"local_dataset_path = \"data/snli_1.0_train_2k.csv\" # download from: https://nlp.stanford.edu/projects/snli/\n",
"\n",

File diff suppressed because one or more lines are too long

@ -37,10 +37,21 @@
"# imports\n",
"import mwclient # for downloading example Wikipedia articles\n",
"import mwparserfromhell # for splitting Wikipedia articles into sections\n",
"import openai # for generating embeddings\n",
"import os # for environment variables\n",
"import pandas as pd # for DataFrames to store article sections and embeddings\n",
"import re # for cutting <ref> links out of Wikipedia articles\n",
"import tiktoken # for counting tokens\n",
"from openai import OpenAI\n",
"\n",
"client = openai.OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Install any missing libraries with `pip install` in your terminal. E.g.,\n",
"\n",
"client = OpenAI()\n"
]
@ -577,8 +588,7 @@
}
],
"source": [
"# calculate embeddings\n",
"EMBEDDING_MODEL = \"text-embedding-ada-002\" # OpenAI's best embeddings as of Apr 2023\n",
"EMBEDDING_MODEL = \"text-embedding-3-small\"\n",
"BATCH_SIZE = 1000 # you can submit up to 2048 embedding inputs per request\n",
"\n",
"embeddings = []\n",

@ -9,7 +9,7 @@
"\n",
"OpenAI's embedding models cannot embed text that exceeds a maximum length. The maximum length varies by model, and is measured by _tokens_, not string length. If you are unfamiliar with tokenization, check out [How to count tokens with tiktoken](How_to_count_tokens_with_tiktoken.ipynb).\n",
"\n",
"This notebook shows how to handle texts that are longer than a model's maximum context length. We'll demonstrate using embeddings from `text-embedding-ada-002`, but the same ideas can be applied to other models and tasks. To learn more about embeddings, check out the [OpenAI Embeddings Guide](https://beta.openai.com/docs/guides/embeddings).\n"
"This notebook shows how to handle texts that are longer than a model's maximum context length. We'll demonstrate using embeddings from `text-embedding-3-small`, but the same ideas can be applied to other models and tasks. To learn more about embeddings, check out the [OpenAI Embeddings Guide](https://beta.openai.com/docs/guides/embeddings).\n"
]
},
{
@ -22,15 +22,25 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"from openai import OpenAI\n",
"client = OpenAI()\n",
"EMBEDDING_MODEL = 'text-embedding-ada-002'\n",
"import os\n",
"import openai\n",
"from tenacity import retry, wait_random_exponential, stop_after_attempt, retry_if_not_exception_type\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n",
"\n",
"EMBEDDING_MODEL = 'text-embedding-3-small'\n",
"EMBEDDING_CTX_LENGTH = 8191\n",
"EMBEDDING_ENCODING = 'cl100k_base'\n"
"EMBEDDING_ENCODING = 'cl100k_base'\n",
"\n",
"# let's make sure to not retry on an invalid request, because that is what we want to demonstrate\n",
"@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6), retry=retry_if_not_exception_type(openai.BadRequestError))\n",
"def get_embedding(text_or_tokens, model=EMBEDDING_MODEL):\n",
" return client.embeddings.create(input=text_or_tokens, model=model).data[0].embedding"
]
},
{
@ -59,12 +69,12 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The `text-embedding-ada-002` model has a context length of 8191 tokens with the `cl100k_base` encoding, and we can see that going over that limit causes an error."
"The `text-embedding-3-small` model has a context length of 8191 tokens with the `cl100k_base` encoding, and we can see that going over that limit causes an error."
]
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 4,
"metadata": {},
"outputs": [
{
@ -80,7 +90,7 @@
"try:\n",
" get_embedding(long_text)\n",
"except openai.BadRequestError as e:\n",
" print(e)\n"
" print(e)"
]
},
{
@ -280,7 +290,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.16"
"version": "3.11.5"
},
"vscode": {
"interpreter": {

@ -44,7 +44,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
@ -53,6 +53,8 @@
"import openai\n",
"import tiktoken\n",
"\n",
"client = openai.OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n",
"\n",
"# Extract the raw text from each PDF using textract\n",
"text = textract.process('data/fia_f1_power_unit_financial_regulations_issue_1_-_2022-08-16.pdf', method='pdfminer').decode('utf-8')\n",
"clean_text = text.replace(\" \", \" \").replace(\"\\n\", \"; \").replace(';',' ')"
@ -67,7 +69,7 @@
},
{
"cell_type": "code",
"execution_count": 28,
"execution_count": 20,
"metadata": {},
"outputs": [
{
@ -96,13 +98,13 @@
"template_prompt=f'''Extract key pieces of information from this regulation document.\n",
"If a particular piece of information is not present, output \\\"Not specified\\\".\n",
"When you extract a key piece of information, include the closest page number.\n",
"Use the following format:\\n0. Who is the author\\n1. What is the amount of the \"Power Unit Cost Cap\" in USD, GBP and EUR\\n2. What is the value of External Manufacturing Costs in USD\\n3. What is the Capital Expenditure Limit in USD\\n\\nDocument: \\\"\\\"\\\"{document}\\\"\\\"\\\"\\n\\n0. Who is the author: Tom Anderson (Page 1)\\n1.'''\n",
"Use the following format:\\n0. Who is the author\\n1. What is the amount of the \"Power Unit Cost Cap\" in USD, GBP and EUR\\n2. What is the value of External Manufacturing Costs in USD\\n3. What is the Capital Expenditure Limit in USD\\n\\nDocument: \\\"\\\"\\\"<document>\\\"\\\"\\\"\\n\\n0. Who is the author: Tom Anderson (Page 1)\\n1.'''\n",
"print(template_prompt)"
]
},
{
"cell_type": "code",
"execution_count": 29,
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
@ -127,19 +129,23 @@
" i = j\n",
"\n",
"def extract_chunk(document,template_prompt):\n",
" \n",
" prompt=template_prompt.replace('<document>',document)\n",
" prompt = template_prompt.replace('<document>',document)\n",
"\n",
" response = openai.Completion.create(\n",
" model='text-davinci-003', \n",
" prompt=prompt,\n",
" temperature=0,\n",
" max_tokens=1500,\n",
" top_p=1,\n",
" frequency_penalty=0,\n",
" presence_penalty=0\n",
" )\n",
" return \"1.\" + response['choices'][0]['text']"
" messages = [\n",
" {\"role\": \"system\", \"content\": \"You help extract information from documents.\"},\n",
" {\"role\": \"user\", \"content\": prompt}\n",
" ]\n",
"\n",
" response = client.chat.completions.create(\n",
" model='gpt-4', \n",
" messages=messages,\n",
" temperature=0,\n",
" max_tokens=1500,\n",
" top_p=1,\n",
" frequency_penalty=0,\n",
" presence_penalty=0\n",
" )\n",
" return \"1.\" + response.choices[0].message.content"
]
},
{
@ -228,7 +234,7 @@
"template_prompt=f'''Extract key pieces of information from this regulation document.\n",
"If a particular piece of information is not present, output \\\"Not specified\\\".\n",
"When you extract a key piece of information, include the closest page number.\n",
"Use the following format:\\n0. Who is the author\\n1. How is a Minor Overspend Breach calculated\\n2. How is a Major Overspend Breach calculated\\n3. Which years do these financial regulations apply to\\n\\nDocument: \\\"\\\"\\\"{document}\\\"\\\"\\\"\\n\\n0. Who is the author: Tom Anderson (Page 1)\\n1.'''\n",
"Use the following format:\\n0. Who is the author\\n1. How is a Minor Overspend Breach calculated\\n2. How is a Major Overspend Breach calculated\\n3. Which years do these financial regulations apply to\\n\\nDocument: \\\"\\\"\\\"<document>\\\"\\\"\\\"\\n\\n0. Who is the author: Tom Anderson (Page 1)\\n1.'''\n",
"print(template_prompt)"
]
},
@ -306,9 +312,9 @@
],
"metadata": {
"kernelspec": {
"display_name": "embed_retrieve",
"display_name": "Python 3",
"language": "python",
"name": "embed_retrieve"
"name": "python3"
},
"language_info": {
"codemirror_mode": {
@ -320,12 +326,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
},
"vscode": {
"interpreter": {
"hash": "5997d090960a54cd76552f75eca12ec3b416cf9d01a1a5af08ae48cf90878791"
}
"version": "3.11.3"
}
},
"nbformat": 4,

File diff suppressed because one or more lines are too long

@ -21,7 +21,7 @@
"metadata": {},
"source": [
"For context, from the function calling notebook above:\n",
"> `functions` is an optional parameter in the Chat Completion API which can be used to provide function specifications. The purpose of this is to enable models to generate function arguments which adhere to the provided specifications. Note that the API will not actually execute any function calls. It is up to developers to execute function calls using model outputs."
"> `tools` is an optional parameter in the Chat Completion API which can be used to provide function specifications. The purpose of this is to enable models to generate function arguments which adhere to the provided specifications. Note that the API will not actually execute any function calls. It is up to developers to execute function calls using model outputs."
]
},
{
@ -33,7 +33,7 @@
"invocations, and incorrect invocations).\\\n",
"Before fine tuning for function calling, it's best to begin with:\n",
"- Improvements to the function definitions. Make them more clear, and more distinct from one another.\n",
"- Experiment with prompt engineering: often a more detailed prompt can help the model call the correct function.\\\n",
"- Experiment with prompt engineering: often a more detailed prompt can help the model call the correct function.\n",
"\n",
"*If* the steps above fail to improve function calling to a satisfactory level, then you can try fine tuning for function calling."
]
@ -76,17 +76,16 @@
"outputs": [],
"source": [
"# !pip install tenacity\n",
"# !pip insta openai\n",
"# !pip install typing\n"
"# !pip install openai\n",
"# !pip install typing"
]
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import openai\n",
"import numpy as np\n",
"import json\n",
"import os\n",
@ -95,7 +94,8 @@
"from tenacity import retry, wait_random_exponential, stop_after_attempt\n",
"from typing import Any, Dict, List, Generator\n",
"import ast\n",
"client = OpenAI()\n"
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))"
]
},
{
@ -114,18 +114,18 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(5))\n",
"def get_chat_completion(\n",
" messages: list[dict[str, str]],\n",
" model: str = \"gpt-4\",\n",
" model: str = \"gpt-3.5-turbo\",\n",
" max_tokens=500,\n",
" temperature=1.0,\n",
" stop=None,\n",
" functions=None,\n",
" tools=None,\n",
" functions=None\n",
") -> str:\n",
" params = {\n",
" 'model': model,\n",
@ -133,6 +133,7 @@
" 'max_tokens': max_tokens,\n",
" 'temperature': temperature,\n",
" 'stop': stop,\n",
" 'tools': tools,\n",
" }\n",
" if functions:\n",
" params['functions'] = functions\n",
@ -159,7 +160,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
@ -177,7 +178,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
@ -430,7 +431,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
@ -456,7 +457,7 @@
"FunctionCall(arguments='{\\n \"altitude\": 50\\n}', name='takeoff_drone') \n",
"\n",
"change speed to 15 kilometers per hour\n",
"FunctionCall(arguments='{\\n \"speed\": 15\\n}', name='set_drone_speed') \n",
"FunctionCall(arguments='{ \"speed\": 15 }', name='set_drone_speed') \n",
"\n",
"turn into an elephant!\n",
"FunctionCall(arguments='{}', name='reject_request') \n",
@ -469,7 +470,7 @@
" messages = []\n",
" messages.append({\"role\": \"system\", \"content\": DRONE_SYSTEM_PROMPT})\n",
" messages.append({\"role\": \"user\", \"content\": prompt})\n",
" completion = get_chat_completion(model=\"gpt-3.5-turbo\",messages=messages,functions=function_list)\n",
" completion = get_chat_completion(model=\"gpt-3.5-turbo\",messages=messages,tools=function_list)\n",
" print(prompt)\n",
" print(completion.function_call,'\\n')\n"
]
@ -508,11 +509,11 @@
"\n",
"\n",
"Initiate live-streaming on social media\n",
"FunctionCall(arguments='{\\n\"mode\": \"video\",\\n\"duration\": 0\\n}', name='control_camera')\n",
"FunctionCall(arguments='{\\n \"mode\": \"video\",\\n \"duration\": 0\\n}', name='control_camera')\n",
"\n",
"\n",
"Scan environment for heat signatures\n",
"None\n",
"FunctionCall(arguments='{\\n \"mode\": \"photo\"\\n}', name='control_camera')\n",
"\n",
"\n",
"Enable stealth mode\n",
@ -531,7 +532,7 @@
" messages = []\n",
" messages.append({\"role\": \"system\", \"content\": DRONE_SYSTEM_PROMPT})\n",
" messages.append({\"role\": \"user\", \"content\": prompt})\n",
" completion = get_chat_completion(model=\"gpt-3.5-turbo\",messages=messages,functions=function_list)\n",
" completion = get_chat_completion(model=\"gpt-3.5-turbo\",messages=messages,tools=function_list)\n",
" print(prompt)\n",
" try:\n",
" print(completion.function_call)\n",
@ -546,7 +547,7 @@
"metadata": {},
"source": [
"Now we run into some problems.\n",
"The model here should reject all of these requests, as they are impossible given the functions, however instead the model calls functions that are somewhat related to the request, but incorrect. The model sets the camera to video when asked to begin 'live streaming to social media', and changes the LED's to blue when asked to 'change the paint color'...\\\n",
"The model here should reject all of these requests, as they are impossible given the functions, however instead the model calls functions that are somewhat related to the request, but incorrect. The model sets the camera to video when asked to begin 'live streaming to social media', and changes the LED's to blue when asked to 'change the paint color'...\n",
"<br>\n",
"In this simple case, more prompt engineering may resolve some of these issues, but for the purpose of this example we will demonstrate how fine tuning can be used to improve performance. Additionally, while this case is relatively straightforward, as the number of and complexity of the functions increases, fine tuning becomes more and more impactful."
]
@ -797,7 +798,7 @@
" \"arguments\": arguments\n",
" }\n",
" messages = [{\"role\": \"user\", \"content\": INVOCATION_FILLER_PROMPT.format(invocation=input_object,function=function)}]\n",
" input_object = get_chat_completion(model='gpt-4',messages=messages, max_tokens = 200,temperature=.1).content\n",
" input_object = get_chat_completion(model='gpt-4', messages=messages, max_tokens = 200, temperature=.1).content\n",
" else:\n",
" input_object = {\n",
" \"name\": func_name,\n",
@ -816,21 +817,22 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"def create_commands(invocation_list):\n",
" example_list = []\n",
" for i, invocation in enumerate(invocation_list):\n",
" print(f'\\033[34m{np.round(100*i/len(invocation_list),1)}% complete\\033[0m')\n",
" print(invocation)\n",
" if i<10:\n",
" print(f'\\033[34m{np.round(100*i/len(invocation_list),1)}% complete\\033[0m')\n",
" print(invocation)\n",
"\n",
" # Format the prompt with the invocation string\n",
" request_prompt = COMMAND_GENERATION_PROMPT.format(invocation=invocation)\n",
"\n",
" messages = [{\"role\": \"user\", \"content\": f\"{request_prompt}\"}]\n",
" completion = get_chat_completion(messages,temperature=0.8).content\n",
" completion = get_chat_completion(messages,temperature=0.8)\n",
" command_dict = {\n",
" \"Input\": invocation,\n",
" \"Prompt\": completion\n",
@ -841,10 +843,38 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 15,
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[34m0.0% complete\u001b[0m\n",
"{'name': 'takeoff_drone', 'arguments': {'altitude': 100}}\n",
"\u001b[34m1.8% complete\u001b[0m\n",
"{'name': 'land_drone', 'arguments': {'location': 'current'}}\n",
"\u001b[34m3.6% complete\u001b[0m\n",
"{'name': 'land_drone', 'arguments': {'location': 'home_base'}}\n",
"\u001b[34m5.4% complete\u001b[0m\n",
"{'name': 'land_drone', 'arguments': {'location': 'custom'}}\n",
"\u001b[34m7.1% complete\u001b[0m\n",
"{'name': 'control_drone_movement', 'arguments': {'direction': 'forward', 'distance': 50}}\n",
"\u001b[34m8.9% complete\u001b[0m\n",
"{'name': 'control_drone_movement', 'arguments': {'direction': 'backward', 'distance': 10}}\n",
"\u001b[34m10.7% complete\u001b[0m\n",
"{'name': 'control_drone_movement', 'arguments': {'direction': 'left', 'distance': 10}}\n",
"\u001b[34m12.5% complete\u001b[0m\n",
"{'name': 'control_drone_movement', 'arguments': {'direction': 'right', 'distance': 10}}\n",
"\u001b[34m14.3% complete\u001b[0m\n",
"{'name': 'control_drone_movement', 'arguments': {'direction': 'up', 'distance': 20}}\n",
"\u001b[34m16.1% complete\u001b[0m\n",
"{'name': 'control_drone_movement', 'arguments': {'direction': 'down', 'distance': 10}}\n"
]
}
],
"source": [
"#Only printing the first 10 rows\n",
"training_examples_unformatted = create_commands(input_objects)\n"
]
},
@ -857,23 +887,25 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"training_examples = []\n",
"\n",
"for prompt in training_examples_unformatted:\n",
" #adjust formatting for training data specs\n",
" try:\n",
" prompt[\"Input\"] = ast.literal_eval(prompt[\"Input\"])\n",
" except:\n",
" continue\n",
"\n",
" #if its not a dict, convert to dict\n",
" if type(prompt['Input'])!=dict:\n",
" prompt['Input'] = ast.literal_eval(prompt['Input'])\n",
" prompt['Input']['arguments']=json.dumps(prompt['Input']['arguments'])\n",
" for p in prompt['Prompt']:\n",
" for p in ast.literal_eval(prompt['Prompt'].content):\n",
" training_examples.append({\"messages\": [{\"role\":\"system\",\"content\":DRONE_SYSTEM_PROMPT\n",
" },{\"role\":\"user\",\"content\": p},\n",
" {\"role\":\"assistant\",\"function_call\": prompt['Input']}],\n",
" \"functions\":function_list})\n"
" \"functions\":function_list})\n",
"\n"
]
},
{
@ -885,7 +917,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
@ -908,7 +940,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
@ -931,7 +963,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
@ -940,7 +972,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
@ -967,9 +999,17 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 21,
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"file-xrLV8EbNZk31QPT1TB5KIx7E\n"
]
}
],
"source": [
"if __name__ == \"__main__\":\n",
" file = client.files.create(\n",
@ -979,7 +1019,6 @@
" file_id = file.id\n",
" print(file_id)\n",
" ft = client.fine_tuning.jobs.create(\n",
" # model=\"gpt-4-0613\",\n",
" model=\"gpt-3.5-turbo\",\n",
" training_file=file_id,\n",
")\n"
@ -1002,15 +1041,37 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 22,
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Play pre-recorded audio message\n",
"FunctionCall(arguments='{}', name='reject_request') \n",
"\n",
"Initiate live-streaming on social media\n",
"FunctionCall(arguments='{}', name='reject_request') \n",
"\n",
"Scan environment for heat signatures\n",
"FunctionCall(arguments='{}', name='reject_request') \n",
"\n",
"Enable stealth mode\n",
"FunctionCall(arguments='{}', name='reject_request') \n",
"\n",
"Change drone's paint job color\n",
"FunctionCall(arguments='{}', name='reject_request') \n",
"\n"
]
}
],
"source": [
"for eval_question in challenging_prompts:\n",
" messages = []\n",
" messages.append({\"role\": \"system\", \"content\": DRONE_SYSTEM_PROMPT})\n",
" messages.append({\"role\": \"user\", \"content\": eval_question})\n",
" completion = get_chat_completion(model=\"ft:gpt-3.5-turbo-0613:openai-internal::8DloQKS2\",messages=messages,functions=function_list)\n",
" completion = get_chat_completion(model=\"ft:gpt-3.5-turbo-0613:openai-internal::8DloQKS2\",messages=messages,tools=function_list)\n",
" print(eval_question)\n",
" print(completion.function_call,'\\n')\n"
]
@ -1053,7 +1114,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.16"
"version": "3.11.5"
}
},
"nbformat": 4,

@ -58,12 +58,11 @@
"outputs": [],
"source": [
"import json\n",
"import openai\n",
"from openai import OpenAI\n",
"import os\n",
"import requests\n",
"\n",
"client = OpenAI()\n"
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))"
]
},
{
@ -228,7 +227,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
@ -252,33 +251,36 @@
" {\"role\": \"user\", \"content\": user_input}\n",
"],\n",
" temperature=0,\n",
" functions=[\n",
" tools=[\n",
" {\n",
" \"name\": \"call_google_places_api\",\n",
" \"description\": \"This function calls the Google Places API to find the top places of a specified type near a specific location. It can be used when a user expresses a need (e.g., feeling hungry or tired) or wants to find a certain type of place (e.g., restaurant or hotel).\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"place_type\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"The type of place to search for.\"\n",
" \"type\": \"function\",\n",
" \"function\" : {\n",
" \"name\": \"call_google_places_api\",\n",
" \"description\": \"This function calls the Google Places API to find the top places of a specified type near a specific location. It can be used when a user expresses a need (e.g., feeling hungry or tired) or wants to find a certain type of place (e.g., restaurant or hotel).\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"place_type\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"The type of place to search for.\"\n",
" }\n",
" }\n",
" },\n",
" \"result\": {\n",
" \"type\": \"array\",\n",
" \"items\": {\n",
" \"type\": \"string\"\n",
" }\n",
" }\n",
" },\n",
" \"result\": {\n",
" \"type\": \"array\",\n",
" \"items\": {\n",
" \"type\": \"string\"\n",
" }\n",
" }\n",
" }\n",
" ],\n",
" )\n",
"\n",
" print(response.choices[0].message.function_call)\n",
" print(response.choices[0].message.tool_calls)\n",
"\n",
" if response.choices[0].finish_reason=='function_call':\n",
" function_call = response.choices[0].message.function_call\n",
" if response.choices[0].finish_reason=='tool_calls':\n",
" function_call = response.choices[0].message.tool_calls[0].function\n",
" if function_call.name == \"call_google_places_api\":\n",
" place_type = json.loads(function_call.arguments)[\"place_type\"]\n",
" places = call_google_places_api(user_id, place_type, food_preference)\n",
@ -302,15 +304,15 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"FunctionCall(arguments='{\\n \"place_type\": \"restaurant\"\\n}', name='call_google_places_api')\n",
"I couldn't find any places of interest nearby.\n"
"[ChatCompletionMessageToolCall(id='call_Q1mXIi7D6GhobfE4tkruX7nB', function=Function(arguments='{\\n \"place_type\": \"restaurant\"\\n}', name='call_google_places_api'), type='function')]\n",
"Here are some places you might be interested in: Sotto Mare is a restaurant located at 552 Green Street. It has a rating of 4.6 based on 3765 user reviews. Mona Lisa Restaurant is a restaurant located at 353 Columbus Avenue #3907. It has a rating of 4.4 based on 1888 user reviews.\n"
]
}
],
@ -338,7 +340,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.16"
"version": "3.11.5"
},
"orig_nbformat": 4
},

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -26,32 +26,30 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"import pandas as pd\n",
"import tiktoken\n",
"\n",
"from utils.embeddings_utils import get_embedding\n"
"from utils.embeddings_utils import get_embedding"
]
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"# embedding model parameters\n",
"embedding_model = \"text-embedding-ada-002\"\n",
"embedding_encoding = \"cl100k_base\" # this the encoding for text-embedding-ada-002\n",
"max_tokens = 8000 # the maximum for text-embedding-ada-002 is 8191\n"
"embedding_model = \"text-embedding-3-small\"\n",
"embedding_encoding = \"cl100k_base\"\n",
"max_tokens = 8000 # the maximum for text-embedding-3-small is 8191"
]
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 17,
"metadata": {},
"outputs": [
{
@ -127,7 +125,7 @@
"1 Title: Arrived in pieces; Content: Not pleased... "
]
},
"execution_count": 8,
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
@ -141,12 +139,12 @@
"df[\"combined\"] = (\n",
" \"Title: \" + df.Summary.str.strip() + \"; Content: \" + df.Text.str.strip()\n",
")\n",
"df.head(2)\n"
"df.head(2)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 18,
"metadata": {},
"outputs": [
{
@ -155,7 +153,7 @@
"1000"
]
},
"execution_count": 9,
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
@ -171,7 +169,7 @@
"# omit reviews that are too long to embed\n",
"df[\"n_tokens\"] = df.combined.apply(lambda x: len(encoding.encode(x)))\n",
"df = df[df.n_tokens <= max_tokens].tail(top_n)\n",
"len(df)\n"
"len(df)"
]
},
{
@ -184,15 +182,24 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"# Ensure you have your API key set in your environment per the README: https://github.com/openai/openai-python#usage\n",
"\n",
"# This may take a few minutes\n",
"df[\"embedding\"] = df.combined.apply(lambda x: get_embedding(x, engine=embedding_model))\n",
"df.to_csv(\"data/fine_food_reviews_with_embeddings_1k.csv\")\n"
"df[\"embedding\"] = df.combined.apply(lambda x: get_embedding(x, model=embedding_model))\n",
"df.to_csv(\"data/fine_food_reviews_with_embeddings_1k.csv\")"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"a = get_embedding(\"hi\", model=embedding_model)"
]
}
],
@ -212,7 +219,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.16"
"version": "3.11.5"
},
"orig_nbformat": 4,
"vscode": {

@ -185,7 +185,7 @@
"source": [
"## LLM Agent\n",
"\n",
"An [LLM agent](https://python.langchain.com/en/latest/modules/agents/agents/custom_llm_agent.html) in Langchain has many configurable components, which are detailed in the Langchain documentation.\n",
"An [LLM agent](https://python.langchain.com/docs/modules/agents/) in Langchain has many configurable components, which are detailed in the Langchain documentation.\n",
"\n",
"We'll employ a few of the core concepts to make an agent that talks in the way we want, can use tools to answer questions, and uses the appropriate language model to power the conversation.\n",
"- **Prompt Template:** The input template to control the LLM's behaviour and how it accepts inputs and produces outputs - this is the brain that drives your application ([docs](https://python.langchain.com/en/latest/modules/prompts/prompt_templates.html)).\n",
@ -662,7 +662,7 @@
"import wget\n",
"\n",
"# Here is a URL to a zip archive containing the transcribed podcasts\n",
"# Note that this data has already been split into chunks and embeddings from OpenAI's text-embedding-ada-002 embedding model are included\n",
"# Note that this data has already been split into chunks and embeddings from OpenAI's `text-embedding-3-small` embedding model are included\n",
"content_url = 'https://cdn.openai.com/API/examples/data/sysk_podcast_transcripts_embedded.json.zip'\n",
"\n",
"# Download the file (it is ~541 MB so this will take some time)\n",

@ -4,10 +4,6 @@ OpenAI functions enable your app to take action based on user inputs. This means
In this tutorial, you will build an app that uses OpenAI functions along with the latest version of the Node.js SDK. The app runs in the browser, so you only need a code editor and, e.g., VS Code Live Server to follow along locally. Alternatively, write your code directly in the browser via [this code playground at Scrimba.](https://scrimba.com/scrim/c6r3LkU9)
If you prefer watching screencasts over reading, then you can check out [this scrim, which walks through the code line-by-line:](https://scrimba.com/scrim/co0044b2d9b9b7f5bf16e0391)
<ScrimPlayer scrimId="co0044b2d9b9b7f5bf16e0391" />
## What you will build
Our app is a simple agent that helps you find activities in your area.
@ -71,34 +67,41 @@ async function getCurrentWeather(latitude, longitude) {
For OpenAI to understand the purpose of these functions, we need to
describe them using a specific schema. We'll create an array called
`functionDefinitions` that contains one object per function. Each object
will have three keys: `name`, `description`, and `parameters`.
`tools` that contains one object per function. Each object
will have two keys: `type`, `function`, and the `function` key has
three subkeys: `name`, `description`, and `parameters`.
```js
const functionDefinitions = [
const tools = [
{
name: "getCurrentWeather",
description: "Get the current weather in a given location",
parameters: {
type: "object",
properties: {
longitude: {
type: "string",
},
latitude: {
type: "string",
type: "function",
function: {
name: "getCurrentWeather",
description: "Get the current weather in a given location",
parameters: {
type: "object",
properties: {
latitude: {
type: "string",
},
longitude: {
type: "string",
},
},
required: ["longitude", "latitude"],
},
required: ["longitude", "latitude"],
},
}
},
{
name: "getLocation",
description: "Get the user's location based on their IP address",
parameters: {
type: "object",
properties: {},
},
type: "function",
function: {
name: "getLocation",
description: "Get the user's location based on their IP address",
parameters: {
type: "object",
properties: {},
},
}
},
];
```
@ -129,16 +132,14 @@ We start by pushing the `userInput` to the messages array. This time, we set the
```js
async function agent(userInput) {
messages.push([
{
role: "user",
content: userInput,
},
]);
messages.push({
role: "user",
content: userInput,
});
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: messages,
functions: functionDefinitions,
tools: tools,
});
console.log(response);
}
@ -153,9 +154,9 @@ properties:
GPT-4).
- `messages` - The entire history of messages between the user and the
AI up until this point.
- `functions` - A description of the functions our app has access to.
Here, we'll we use the `functionDefinitions` array we created
earlier.
- `tools` - A list of tools the model may call. Currently, only
functions are supported as a tool., we'll we use the `tools` array we
created earlier.
## Running our app with a simple input
@ -179,35 +180,41 @@ to the console like this:
message: {
role: "assistant",
content: null,
function_call: {
name: "getLocation", // The function OpenAI wants us to call
tool_calls: [
id: "call_CBwbo9qoXUn1kTR5pPuv6vR1",
type: "function",
function: {
name: "getLocation",
arguments: "{}"
}
}
]
},
finish_reason: "function_call" // OpenAI wants us to call a function
logprobs: null,
finish_reason: "tool_calls" // OpenAI wants us to call a function
}],
usage: {
prompt_tokens: 134,
completion_tokens: 6,
total_tokens: 140
}
system_fingerprint: null
}
```
This response tells us that we should call one of our functions, as it contains the following key: `finish:_reason: "function_call"`.
This response tells us that we should call one of our functions, as it contains the following key: `finish_reason: "tool_calls"`.
The name of the function can be found in the
`response.choices[0].message.function_call.name` key, which is set to
`response.choices[0].message.tool_calls[0].function.name` key, which is set to
`"getLocation"`.
## Turning the OpenAI response into a function call
Now that we have the name of the function as a string, we'll need to
translate that into a function call. To help us with that, we'll gather
both of our functions in an object called `availableFunctions`:
both of our functions in an object called `availableTools`:
```js
const availableFunctions = {
const availableTools = {
getCurrentWeather,
getLocation,
};
@ -215,15 +222,15 @@ const availableFunctions = {
This is handy because we'll be able to access the `getLocation` function
via bracket notation and the string we got back from OpenAI, like this:
`availableFunctions["getLocation"]`.
`availableTools["getLocation"]`.
```js
const { finish_reason, message } = response.choices[0];
if (finish_reason === "function_call") {
const functionName = message.function_call.name;
const functionToCall = availableFunctions[functionName];
const functionArgs = JSON.parse(message.function_call.arguments);
if (finish_reason === "tool_calls" && message.tool_calls) {
const functionName = message.tool_calls[0].function.name;
const functionToCall = availableTools[functionName];
const functionArgs = JSON.parse(message.tool_calls[0].function.arguments);
const functionArgsArr = Object.values(functionArgs);
const functionResponse = await functionToCall.apply(null, functionArgsArr);
console.log(functionResponse);
@ -231,7 +238,7 @@ if (finish_reason === "function_call") {
```
We're also grabbing ahold of any arguments OpenAI wants us to pass into
the function: `message.function_call.arguments`.
the function: `message.tool_calls[0].function.arguments`.
However, we won't need any arguments for this first function call.
If we run the code again with the same input
@ -275,7 +282,7 @@ simplicity.
At the top of the `agent` function, we'll create a loop that lets us run
the entire procedure up to five times.
If we get back `finish_reason: "function_call"` from GPT, we'll just
If we get back `finish_reason: "tool_calls"` from GPT, we'll just
push the result of the function call to the `messages` array and jump to
the next iteration of the loop, triggering a new request.
@ -287,14 +294,14 @@ for (let i = 0; i < 5; i++) {
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: messages,
functions: functionDefinitions,
tools: tools,
});
const { finish_reason, message } = response.choices[0];
if (finish_reason === "function_call") {
const functionName = message.function_call.name;
const functionToCall = availableFunctions[functionName];
const functionArgs = JSON.parse(message.function_call.arguments);
if (finish_reason === "tool_calls" && message.tool_calls) {
const functionName = message.tool_calls[0].function.name;
const functionToCall = availableTools[functionName];
const functionArgs = JSON.parse(message.tool_calls[0].function.arguments);
const functionArgsArr = Object.values(functionArgs);
const functionResponse = await functionToCall.apply(null, functionArgsArr);
@ -356,8 +363,8 @@ to call the `getCurrentWeather` function with
arguments. This is data it got back from the first function call we did.
```js
{role: "assistant", content: null, function_call: {name: "getLocation", arguments: "{}"}}
{role: "assistant", content: null, function_call: {name: "getCurrentWeather", arguments: " { "longitude": "10.859", "latitude": "59.955" }"}}
{"role":"assistant","content":null,"tool_calls":[{"id":"call_Cn1KH8mtHQ2AMbyNwNJTweEP","type":"function","function":{"name":"getLocation","arguments":"{}"}}]}
{"role":"assistant","content":null,"tool_calls":[{"id":"call_uc1oozJfGTvYEfIzzcsfXfOl","type":"function","function":{"name":"getCurrentWeather","arguments":"{\n\"latitude\": \"10.859\",\n\"longitude\": \"59.955\"\n}"}}]}
```
You've now built an AI agent using OpenAI functions and the Node.js SDK! If you're looking for an extra challenge, consider enhancing this app. For example, you could add a function that fetches up-to-date information on events and activities in the user's location.
@ -388,35 +395,40 @@ async function getCurrentWeather(latitude, longitude) {
return weatherData;
}
const functionDefinitions = [
const tools = [
{
name: "getCurrentWeather",
description:
"Get the current weather in a given location given in latitude and longitude",
parameters: {
type: "object",
properties: {
latitude: {
type: "string",
},
longitude: {
type: "string",
type: "function",
function: {
name: "getCurrentWeather",
description: "Get the current weather in a given location",
parameters: {
type: "object",
properties: {
latitude: {
type: "string",
},
longitude: {
type: "string",
},
},
required: ["longitude", "latitude"],
},
required: ["longitude", "latitude"],
},
}
},
{
name: "getLocation",
description: "Get the user's location based on their IP address",
parameters: {
type: "object",
properties: {},
},
type: "function",
function: {
name: "getLocation",
description: "Get the user's location based on their IP address",
parameters: {
type: "object",
properties: {},
},
}
},
];
const availableFunctions = {
const availableTools = {
getCurrentWeather,
getLocation,
};
@ -438,15 +450,15 @@ async function agent(userInput) {
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: messages,
functions: functionDefinitions,
tools: tools,
});
const { finish_reason, message } = response.choices[0];
if (finish_reason === "function_call") {
const functionName = message.function_call.name;
const functionToCall = availableFunctions[functionName];
const functionArgs = JSON.parse(message.function_call.arguments);
if (finish_reason === "tool_calls" && message.tool_calls) {
const functionName = message.tool_calls[0].function.name;
const functionToCall = availableTools[functionName];
const functionArgs = JSON.parse(message.tool_calls[0].function.arguments);
const functionArgsArr = Object.values(functionArgs);
const functionResponse = await functionToCall.apply(
null,

@ -39,94 +39,47 @@
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0mRequirement already satisfied: scipy in /opt/homebrew/lib/python3.11/site-packages (1.10.1)\n",
"Requirement already satisfied: numpy<1.27.0,>=1.19.5 in /opt/homebrew/lib/python3.11/site-packages (from scipy) (1.24.3)\n",
"\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0mRequirement already satisfied: tenacity in /opt/homebrew/lib/python3.11/site-packages (8.2.2)\n",
"\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0mRequirement already satisfied: tiktoken in /opt/homebrew/lib/python3.11/site-packages (0.4.0)\n",
"Requirement already satisfied: regex>=2022.1.18 in /opt/homebrew/lib/python3.11/site-packages (from tiktoken) (2023.6.3)\n",
"Requirement already satisfied: requests>=2.26.0 in /opt/homebrew/lib/python3.11/site-packages (from tiktoken) (2.30.0)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /opt/homebrew/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken) (3.1.0)\n",
"Requirement already satisfied: idna<4,>=2.5 in /opt/homebrew/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken) (3.4)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/homebrew/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken) (1.25.11)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /opt/homebrew/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken) (2023.5.7)\n",
"\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0mRequirement already satisfied: termcolor in /opt/homebrew/lib/python3.11/site-packages (2.3.0)\n",
"\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0mRequirement already satisfied: openai in /opt/homebrew/lib/python3.11/site-packages (0.27.6)\n",
"Requirement already satisfied: requests>=2.20 in /opt/homebrew/lib/python3.11/site-packages (from openai) (2.30.0)\n",
"Requirement already satisfied: tqdm in /opt/homebrew/lib/python3.11/site-packages (from openai) (4.65.0)\n",
"Requirement already satisfied: aiohttp in /opt/homebrew/lib/python3.11/site-packages (from openai) (3.8.4)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /opt/homebrew/lib/python3.11/site-packages (from requests>=2.20->openai) (3.1.0)\n",
"Requirement already satisfied: idna<4,>=2.5 in /opt/homebrew/lib/python3.11/site-packages (from requests>=2.20->openai) (3.4)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/homebrew/lib/python3.11/site-packages (from requests>=2.20->openai) (1.25.11)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /opt/homebrew/lib/python3.11/site-packages (from requests>=2.20->openai) (2023.5.7)\n",
"Requirement already satisfied: attrs>=17.3.0 in /opt/homebrew/lib/python3.11/site-packages (from aiohttp->openai) (23.1.0)\n",
"Requirement already satisfied: multidict<7.0,>=4.5 in /opt/homebrew/lib/python3.11/site-packages (from aiohttp->openai) (6.0.4)\n",
"Requirement already satisfied: async-timeout<5.0,>=4.0.0a3 in /opt/homebrew/lib/python3.11/site-packages (from aiohttp->openai) (4.0.2)\n",
"Requirement already satisfied: yarl<2.0,>=1.0 in /opt/homebrew/lib/python3.11/site-packages (from aiohttp->openai) (1.9.2)\n",
"Requirement already satisfied: frozenlist>=1.1.1 in /opt/homebrew/lib/python3.11/site-packages (from aiohttp->openai) (1.3.3)\n",
"Requirement already satisfied: aiosignal>=1.1.2 in /opt/homebrew/lib/python3.11/site-packages (from aiohttp->openai) (1.3.1)\n",
"\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0mRequirement already satisfied: requests in /opt/homebrew/lib/python3.11/site-packages (2.30.0)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /opt/homebrew/lib/python3.11/site-packages (from requests) (3.1.0)\n",
"Requirement already satisfied: idna<4,>=2.5 in /opt/homebrew/lib/python3.11/site-packages (from requests) (3.4)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/homebrew/lib/python3.11/site-packages (from requests) (1.25.11)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /opt/homebrew/lib/python3.11/site-packages (from requests) (2023.5.7)\n",
"\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0mRequirement already satisfied: arxiv in /opt/homebrew/lib/python3.11/site-packages (1.4.7)\n",
"Requirement already satisfied: feedparser in /opt/homebrew/lib/python3.11/site-packages (from arxiv) (6.0.10)\n",
"Requirement already satisfied: sgmllib3k in /opt/homebrew/lib/python3.11/site-packages (from feedparser->arxiv) (1.0.0)\n",
"\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[0mRequirement already satisfied: pandas in /opt/homebrew/lib/python3.11/site-packages (2.0.1)\n",
"Requirement already satisfied: python-dateutil>=2.8.2 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (2.8.2)\n",
"Requirement already satisfied: pytz>=2020.1 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (2023.3)\n",
"Requirement already satisfied: tzdata>=2022.1 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (2023.3)\n",
"Requirement already satisfied: numpy>=1.21.0 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (1.24.3)\n",
"Requirement already satisfied: six>=1.5 in /opt/homebrew/lib/python3.11/site-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)\n",
"\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0mRequirement already satisfied: PyPDF2 in /opt/homebrew/lib/python3.11/site-packages (3.0.1)\n",
"\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0mRequirement already satisfied: tqdm in /opt/homebrew/lib/python3.11/site-packages (4.65.0)\n",
"\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m\u001b[33mWARNING: Skipping /opt/homebrew/lib/python3.11/site-packages/PyYAML-6.0-py3.11.egg-info due to invalid metadata entry 'name'\u001b[0m\u001b[33m\n",
"\u001b[0m"
"Requirement already satisfied: scipy in /usr/local/lib/python3.11/site-packages (1.12.0)\n",
"Requirement already satisfied: numpy<1.29.0,>=1.22.4 in /usr/local/lib/python3.11/site-packages (from scipy) (1.26.3)\n",
"Requirement already satisfied: tenacity in /usr/local/lib/python3.11/site-packages (8.2.3)\n",
"Requirement already satisfied: tiktoken==0.3.3 in /usr/local/lib/python3.11/site-packages (0.3.3)\n",
"Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.11/site-packages (from tiktoken==0.3.3) (2023.12.25)\n",
"Requirement already satisfied: requests>=2.26.0 in /usr/local/lib/python3.11/site-packages (from tiktoken==0.3.3) (2.31.0)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken==0.3.3) (3.3.2)\n",
"Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken==0.3.3) (3.6)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken==0.3.3) (2.1.0)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken==0.3.3) (2023.11.17)\n",
"Requirement already satisfied: termcolor in /usr/local/lib/python3.11/site-packages (2.4.0)\n",
"Requirement already satisfied: openai in /usr/local/lib/python3.11/site-packages (1.10.0)\n",
"Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.11/site-packages (from openai) (4.2.0)\n",
"Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.11/site-packages (from openai) (1.9.0)\n",
"Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.11/site-packages (from openai) (0.26.0)\n",
"Requirement already satisfied: pydantic<3,>=1.9.0 in /usr/local/lib/python3.11/site-packages (from openai) (2.5.3)\n",
"Requirement already satisfied: sniffio in /usr/local/lib/python3.11/site-packages (from openai) (1.3.0)\n",
"Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.11/site-packages (from openai) (4.66.1)\n",
"Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.11/site-packages (from openai) (4.9.0)\n",
"Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.11/site-packages (from anyio<5,>=3.5.0->openai) (3.6)\n",
"Requirement already satisfied: certifi in /usr/local/lib/python3.11/site-packages (from httpx<1,>=0.23.0->openai) (2023.11.17)\n",
"Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.11/site-packages (from httpx<1,>=0.23.0->openai) (1.0.2)\n",
"Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.11/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.14.0)\n",
"Requirement already satisfied: annotated-types>=0.4.0 in /usr/local/lib/python3.11/site-packages (from pydantic<3,>=1.9.0->openai) (0.6.0)\n",
"Requirement already satisfied: pydantic-core==2.14.6 in /usr/local/lib/python3.11/site-packages (from pydantic<3,>=1.9.0->openai) (2.14.6)\n",
"Requirement already satisfied: arxiv in /usr/local/lib/python3.11/site-packages (2.1.0)\n",
"Requirement already satisfied: feedparser==6.0.10 in /usr/local/lib/python3.11/site-packages (from arxiv) (6.0.10)\n",
"Requirement already satisfied: requests==2.31.0 in /usr/local/lib/python3.11/site-packages (from arxiv) (2.31.0)\n",
"Requirement already satisfied: sgmllib3k in /usr/local/lib/python3.11/site-packages (from feedparser==6.0.10->arxiv) (1.0.0)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/site-packages (from requests==2.31.0->arxiv) (3.3.2)\n",
"Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/site-packages (from requests==2.31.0->arxiv) (3.6)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/site-packages (from requests==2.31.0->arxiv) (2.1.0)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/site-packages (from requests==2.31.0->arxiv) (2023.11.17)\n",
"Requirement already satisfied: pandas in /usr/local/lib/python3.11/site-packages (2.2.0)\n",
"Requirement already satisfied: numpy<2,>=1.23.2 in /usr/local/lib/python3.11/site-packages (from pandas) (1.26.3)\n",
"Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.11/site-packages (from pandas) (2.8.2)\n",
"Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/site-packages (from pandas) (2023.3.post1)\n",
"Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/site-packages (from pandas) (2023.4)\n",
"Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/site-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)\n",
"Requirement already satisfied: PyPDF2 in /usr/local/lib/python3.11/site-packages (3.0.1)\n",
"Requirement already satisfied: tqdm in /usr/local/lib/python3.11/site-packages (4.66.1)\n"
]
}
],
@ -136,7 +89,6 @@
"!pip install tiktoken==0.3.3\n",
"!pip install termcolor \n",
"!pip install openai\n",
"!pip install requests\n",
"!pip install arxiv\n",
"!pip install pandas\n",
"!pip install PyPDF2\n",
@ -151,26 +103,25 @@
"outputs": [],
"source": [
"import os\n",
"\n",
"import arxiv\n",
"import ast\n",
"import concurrent\n",
"from csv import writer\n",
"from IPython.display import display, Markdown, Latex\n",
"import json\n",
"import openai\n",
"import os\n",
"import pandas as pd\n",
"import tiktoken\n",
"from csv import writer\n",
"from IPython.display import display, Markdown, Latex\n",
"from openai import OpenAI\n",
"from PyPDF2 import PdfReader\n",
"import requests\n",
"from scipy import spatial\n",
"from tenacity import retry, wait_random_exponential, stop_after_attempt\n",
"import tiktoken\n",
"from tqdm import tqdm\n",
"from termcolor import colored\n",
"\n",
"GPT_MODEL = \"gpt-3.5-turbo-0613\"\n",
"EMBEDDING_MODEL = \"text-embedding-ada-002\"\n"
"EMBEDDING_MODEL = \"text-embedding-ada-002\"\n",
"client = OpenAI()"
]
},
{
@ -191,7 +142,15 @@
"execution_count": 3,
"id": "2de5d32d",
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Directory './data/papers' already exists.\n"
]
}
],
"source": [
"directory = './data/papers'\n",
"\n",
@ -207,7 +166,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 4,
"id": "ae5cb7a1",
"metadata": {},
"outputs": [],
@ -223,26 +182,30 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 5,
"id": "57217b9d",
"metadata": {},
"outputs": [],
"source": [
"@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3))\n",
"def embedding_request(text):\n",
" response = openai.Embedding.create(input=text, model=EMBEDDING_MODEL)\n",
" response = client.embeddings.create(input=text, model=EMBEDDING_MODEL)\n",
" return response\n",
"\n",
"\n",
"@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3))\n",
"def get_articles(query, library=paper_dir_filepath, top_k=5):\n",
" \"\"\"This function gets the top_k articles based on a user's query, sorted by relevance.\n",
" It also downloads the files and stores them in arxiv_library.csv to be retrieved by the read_article_and_summarize.\n",
" \"\"\"\n",
" client = arxiv.Client()\n",
" search = arxiv.Search(\n",
" query=query, max_results=top_k, sort_by=arxiv.SortCriterion.Relevance\n",
" query = \"quantum\",\n",
" max_results = 10,\n",
" sort_by = arxiv.SortCriterion.SubmittedDate\n",
" )\n",
" result_list = []\n",
" for result in search.results():\n",
" for result in client.results(search):\n",
" result_dict = {}\n",
" result_dict.update({\"title\": result.title})\n",
" result_dict.update({\"summary\": result.summary})\n",
@ -257,7 +220,7 @@
" file_reference = [\n",
" result.title,\n",
" result.download_pdf(data_dir),\n",
" response[\"data\"][0][\"embedding\"],\n",
" response.data[0].embedding,\n",
" ]\n",
"\n",
" # Write to file\n",
@ -270,20 +233,20 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 6,
"id": "dda02bdb",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'title': 'Proximal Policy Optimization and its Dynamic Version for Sequence Generation',\n",
" 'summary': 'In sequence generation task, many works use policy gradient for model\\noptimization to tackle the intractable backpropagation issue when maximizing\\nthe non-differentiable evaluation metrics or fooling the discriminator in\\nadversarial learning. In this paper, we replace policy gradient with proximal\\npolicy optimization (PPO), which is a proved more efficient reinforcement\\nlearning algorithm, and propose a dynamic approach for PPO (PPO-dynamic). We\\ndemonstrate the efficacy of PPO and PPO-dynamic on conditional sequence\\ngeneration tasks including synthetic experiment and chit-chat chatbot. The\\nresults show that PPO and PPO-dynamic can beat policy gradient by stability and\\nperformance.',\n",
" 'article_url': 'http://arxiv.org/abs/1808.07982v1',\n",
" 'pdf_url': 'http://arxiv.org/pdf/1808.07982v1'}"
"{'title': 'Entanglement entropy and deconfined criticality: emergent SO(5) symmetry and proper lattice bipartition',\n",
" 'summary': \"We study the R\\\\'enyi entanglement entropy (EE) of the two-dimensional $J$-$Q$\\nmodel, the emblematic quantum spin model of deconfined criticality at the phase\\ntransition between antiferromagnetic and valence-bond-solid ground states.\\nQuantum Monte Carlo simulations with an improved EE scheme reveal critical\\ncorner contributions that scale logarithmically with the system size, with a\\ncoefficient in remarkable agreement with the form expected from a large-$N$\\nconformal field theory with SO($N=5$) symmetry. However, details of the\\nbipartition of the lattice are crucial in order to observe this behavior. If\\nthe subsystem for the reduced density matrix does not properly accommodate\\nvalence-bond fluctuations, logarithmic contributions appear even for\\ncorner-less bipartitions. We here use a $45^\\\\circ$ tilted cut on the square\\nlattice. Beyond supporting an SO($5$) deconfined quantum critical point, our\\nresults for both the regular and tilted cuts demonstrate important microscopic\\naspects of the EE that are not captured by conformal field theory.\",\n",
" 'article_url': 'http://arxiv.org/abs/2401.14396v1',\n",
" 'pdf_url': 'http://arxiv.org/pdf/2401.14396v1'}"
]
},
"execution_count": 5,
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
@ -296,7 +259,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 7,
"id": "11675627",
"metadata": {},
"outputs": [],
@ -309,7 +272,7 @@
") -> list[str]:\n",
" \"\"\"Returns a list of strings and relatednesses, sorted from most related to least.\"\"\"\n",
" query_embedding_response = embedding_request(query)\n",
" query_embedding = query_embedding_response[\"data\"][0][\"embedding\"]\n",
" query_embedding = query_embedding_response.data[0].embedding\n",
" strings_and_relatednesses = [\n",
" (row[\"filepath\"], relatedness_fn(query_embedding, row[\"embedding\"]))\n",
" for i, row in df.iterrows()\n",
@ -321,7 +284,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 8,
"id": "7211df2c",
"metadata": {},
"outputs": [],
@ -362,10 +325,10 @@
"def extract_chunk(content, template_prompt):\n",
" \"\"\"This function applies a prompt to some input content. In this case it returns a summarized chunk of text\"\"\"\n",
" prompt = template_prompt + content\n",
" response = openai.ChatCompletion.create(\n",
" response = client.chat.completions.create(\n",
" model=GPT_MODEL, messages=[{\"role\": \"user\", \"content\": prompt}], temperature=0\n",
" )\n",
" return response[\"choices\"][0][\"message\"][\"content\"]\n",
" return response.choices[0].message.content\n",
"\n",
"\n",
"def summarize_text(query):\n",
@ -418,7 +381,7 @@
"\n",
" # Final summary\n",
" print(\"Summarizing into overall summary\")\n",
" response = openai.ChatCompletion.create(\n",
" response = client.chat.completions.create(\n",
" model=GPT_MODEL,\n",
" messages=[\n",
" {\n",
@ -437,7 +400,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 9,
"id": "898b94d4",
"metadata": {},
"outputs": [
@ -453,7 +416,7 @@
"name": "stderr",
"output_type": "stream",
"text": [
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:04<00:00, 1.19s/it]\n"
"100%|██████████| 15/15 [00:08<00:00, 1.76it/s]\n"
]
},
{
@ -471,7 +434,7 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 10,
"id": "c715f60d",
"metadata": {},
"outputs": [
@ -479,26 +442,12 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Core Argument:\n",
"- The paper discusses the use of Proximal Policy Optimization (PPO) in sequence generation tasks, specifically in the context of chit-chat chatbots.\n",
"- The authors argue that PPO is a more efficient reinforcement learning algorithm compared to policy gradient, commonly used in text generation tasks.\n",
"- They propose a dynamic approach for PPO (PPO-dynamic) and demonstrate its efficacy in synthetic experiments and chit-chat chatbot tasks.\n",
"\n",
"Evidence:\n",
"- PPO-dynamic achieves high precision scores comparable to other algorithms in a synthetic counting task.\n",
"- PPO-dynamic shows faster progress and more stable learning curves compared to PPO in the synthetic counting task.\n",
"- In the chit-chat chatbot task, PPO-dynamic achieves a slightly higher BLEU-2 score than other algorithms.\n",
"- PPO and PPO-dynamic have more stable learning curves and converge faster than policy gradient.\n",
"\n",
"Conclusions:\n",
"- PPO is a better optimization method for sequence learning compared to policy gradient.\n",
"- PPO-dynamic further improves the optimization process by dynamically adjusting hyperparameters.\n",
"- PPO can be used as a new optimization method for GAN-based sequence learning for better performance.\n"
"The academic paper discusses the unique decomposition of generators of completely positive dynamical semigroups in infinite dimensions. The main result of the paper is that for any separable complex Hilbert space, any trace-class operator B that does not have a purely imaginary trace, and any generator L of a norm-continuous one-parameter semigroup of completely positive maps, there exists a unique bounded operator K and a unique completely positive map Φ such that L=K(·) + (·)K+ Φ. The paper also introduces a modified version of the Choi formalism, which relates completely positive maps to positive semi-definite operators, and characterizes when this correspondence is injective and surjective. The paper concludes by discussing the challenges and questions that arise when generalizing the results to non-separable Hilbert spaces.\n"
]
}
],
"source": [
"print(chat_test_response[\"choices\"][0][\"message\"][\"content\"])\n"
"print(chat_test_response.choices[0].message.content)\n"
]
},
{
@ -514,25 +463,18 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 11,
"id": "77a6fb4f",
"metadata": {},
"outputs": [],
"source": [
"@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3))\n",
"def chat_completion_request(messages, functions=None, model=GPT_MODEL):\n",
" headers = {\n",
" \"Content-Type\": \"application/json\",\n",
" \"Authorization\": \"Bearer \" + openai.api_key,\n",
" }\n",
" json_data = {\"model\": model, \"messages\": messages}\n",
" if functions is not None:\n",
" json_data.update({\"functions\": functions})\n",
" try:\n",
" response = requests.post(\n",
" \"https://api.openai.com/v1/chat/completions\",\n",
" headers=headers,\n",
" json=json_data,\n",
" response = client.chat.completions.create(\n",
" model=model,\n",
" messages=messages,\n",
" functions=functions,\n",
" )\n",
" return response\n",
" except Exception as e:\n",
@ -543,7 +485,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 12,
"id": "73f7672d",
"metadata": {},
"outputs": [],
@ -574,7 +516,7 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 13,
"id": "978b7877",
"metadata": {},
"outputs": [],
@ -619,7 +561,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 14,
"id": "0c88ae15",
"metadata": {},
"outputs": [],
@ -627,23 +569,23 @@
"def chat_completion_with_function_execution(messages, functions=[None]):\n",
" \"\"\"This function makes a ChatCompletion API call with the option of adding functions\"\"\"\n",
" response = chat_completion_request(messages, functions)\n",
" full_message = response.json()[\"choices\"][0]\n",
" if full_message[\"finish_reason\"] == \"function_call\":\n",
" full_message = response.choices[0]\n",
" if full_message.finish_reason == \"function_call\":\n",
" print(f\"Function generation requested, calling function\")\n",
" return call_arxiv_function(messages, full_message)\n",
" else:\n",
" print(f\"Function not required, responding to user\")\n",
" return response.json()\n",
" return response\n",
"\n",
"\n",
"def call_arxiv_function(messages, full_message):\n",
" \"\"\"Function calling function which executes function calls when the model believes it is necessary.\n",
" Currently extended by adding clauses to this if statement.\"\"\"\n",
"\n",
" if full_message[\"message\"][\"function_call\"][\"name\"] == \"get_articles\":\n",
" if full_message.message.function_call.name == \"get_articles\":\n",
" try:\n",
" parsed_output = json.loads(\n",
" full_message[\"message\"][\"function_call\"][\"arguments\"]\n",
" full_message.message.function_call.arguments\n",
" )\n",
" print(\"Getting search results\")\n",
" results = get_articles(parsed_output[\"query\"])\n",
@ -654,23 +596,23 @@
" messages.append(\n",
" {\n",
" \"role\": \"function\",\n",
" \"name\": full_message[\"message\"][\"function_call\"][\"name\"],\n",
" \"name\": full_message.message.function_call.name,\n",
" \"content\": str(results),\n",
" }\n",
" )\n",
" try:\n",
" print(\"Got search results, summarizing content\")\n",
" response = chat_completion_request(messages)\n",
" return response.json()\n",
" return response\n",
" except Exception as e:\n",
" print(type(e))\n",
" raise Exception(\"Function chat request failed\")\n",
"\n",
" elif (\n",
" full_message[\"message\"][\"function_call\"][\"name\"] == \"read_article_and_summarize\"\n",
" full_message.message.function_call.name == \"read_article_and_summarize\"\n",
" ):\n",
" parsed_output = json.loads(\n",
" full_message[\"message\"][\"function_call\"][\"arguments\"]\n",
" full_message.message.function_call.arguments\n",
" )\n",
" print(\"Finding and reading paper\")\n",
" summary = summarize_text(parsed_output[\"query\"])\n",
@ -693,7 +635,7 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 15,
"id": "c39a1d80",
"metadata": {},
"outputs": [],
@ -709,7 +651,7 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 16,
"id": "253fd0f7",
"metadata": {},
"outputs": [
@ -718,43 +660,30 @@
"output_type": "stream",
"text": [
"Function generation requested, calling function\n",
"Finding and reading paper\n",
"Chunking text from paper\n",
"Summarizing each chunk of text\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████████████████████████████████████████████████████████████████████████████| 17/17 [00:06<00:00, 2.65it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Summarizing into overall summary\n"
"Getting search results\n",
"Got search results, summarizing content\n"
]
},
{
"data": {
"text/markdown": [
"Core Argument:\n",
"- The paper focuses on the theoretical analysis of the PPO-Clip algorithm in the context of deep reinforcement learning.\n",
"- The authors propose two core ideas: reinterpreting PPO-Clip from the perspective of hinge loss and introducing a two-step policy improvement scheme.\n",
"- The paper establishes the global convergence of PPO-Clip and characterizes its convergence rate.\n",
"PPO (Proximal Policy Optimization) is a reinforcement learning algorithm used in training agents to make sequential decisions in dynamic environments. It belongs to the family of policy optimization algorithms and addresses the challenge of optimizing policies in a stable and sample-efficient manner. \n",
"\n",
"PPO works by iteratively collecting a batch of data from interacting with the environment, computing advantages to estimate the quality of actions, and then performing multiple policy updates using a clipped surrogate objective. This objective function helps prevent excessive policy updates that could lead to policy divergence and instability. \n",
"\n",
"By iteratively updating the policy using the collected data, PPO seeks to maximize the expected cumulative rewards obtained by the agent. It has been used successfully in a variety of reinforcement learning tasks, including robotic control, game playing, and simulated environments. \n",
"\n",
"To learn more about PPO reinforcement learning, you can read the following papers:\n",
"\n",
"Evidence:\n",
"- The paper addresses the challenges posed by the clipping mechanism and neural function approximation.\n",
"- The authors provide theoretical proofs, lemmas, and mathematical analysis to support their arguments.\n",
"- The paper presents empirical experiments on various reinforcement learning benchmark tasks to validate the effectiveness of PPO-Clip.\n",
"1. Title: \"Proximal Policy Optimization Algorithms\"\n",
" Article URL: [arxiv.org/abs/1707.06347v2](http://arxiv.org/abs/1707.06347v2)\n",
" Summary: This paper introduces PPO and presents two versions of the algorithm: PPO-Penalty and PPO-Clip. It provides a detailed description of PPO's update rule and compares its performance against other popular reinforcement learning algorithms.\n",
"\n",
"Conclusions:\n",
"- The paper offers theoretical insights into the performance of PPO-Clip and provides a framework for analyzing its convergence properties.\n",
"- PPO-Clip is shown to have a global convergence rate of O(1/sqrt(T)), where T is the number of iterations.\n",
"- The hinge loss reinterpretation of PPO-Clip allows for variants with comparable empirical performance.\n",
"- The paper contributes to a better understanding of PPO-Clip in the reinforcement learning community."
"2. Title: \"Emergent Properties of PPO Reinforcement Learning in Resource-Limited Environments\"\n",
" Article URL: [arxiv.org/abs/2001.14342v1](http://arxiv.org/abs/2001.14342v1)\n",
" Summary: This paper explores the emergent properties of PPO reinforcement learning algorithms in resource-limited environments. It discusses the impact of varying the resource constraints and agent population sizes on the learning process and performance.\n",
"\n",
"Reading these papers will give you a deeper understanding of PPO reinforcement learning and its applications in different domains."
],
"text/plain": [
"<IPython.core.display.Markdown object>"
@ -770,14 +699,14 @@
"chat_response = chat_completion_with_function_execution(\n",
" paper_conversation.conversation_history, functions=arxiv_functions\n",
")\n",
"assistant_message = chat_response[\"choices\"][0][\"message\"][\"content\"]\n",
"assistant_message = chat_response.choices[0].message.content\n",
"paper_conversation.add_message(\"assistant\", assistant_message)\n",
"display(Markdown(assistant_message))\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 17,
"id": "3ca3e18a",
"metadata": {},
"outputs": [
@ -795,7 +724,7 @@
"name": "stderr",
"output_type": "stream",
"text": [
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:04<00:00, 1.08s/it]\n"
"100%|██████████| 15/15 [00:09<00:00, 1.67it/s]\n"
]
},
{
@ -808,21 +737,7 @@
{
"data": {
"text/markdown": [
"Core Argument:\n",
"- The paper discusses the use of proximal policy optimization (PPO) in sequence generation tasks, specifically in the context of chit-chat chatbots.\n",
"- The authors argue that PPO is a more efficient reinforcement learning algorithm compared to policy gradient, which is commonly used in text generation tasks.\n",
"- They propose a dynamic approach for PPO (PPO-dynamic) and demonstrate its efficacy in synthetic experiments and chit-chat chatbot tasks.\n",
"\n",
"Evidence:\n",
"- The authors derive the constraints for PPO-dynamic and provide the pseudo code for both PPO and PPO-dynamic.\n",
"- They compare the performance of PPO-dynamic with other algorithms, including REINFORCE, MIXER, and SeqGAN, on a synthetic counting task and a chit-chat chatbot task using the OpenSubtitles dataset.\n",
"- In the synthetic counting task, PPO-dynamic achieves a high precision score comparable to REINFORCE and MIXER, with a faster learning curve compared to PPO.\n",
"- In the chit-chat chatbot task, PPO-dynamic achieves a slightly higher BLEU-2 score than REINFORCE and PPO, with a more stable and faster learning curve than policy gradient.\n",
"\n",
"Conclusions:\n",
"- The results suggest that PPO is a better optimization method for sequence learning compared to policy gradient.\n",
"- PPO-dynamic further improves the optimization process by dynamically adjusting the hyperparameters.\n",
"- The authors conclude that PPO can be used as a new optimization method for GAN-based sequence learning for better performance."
"The paper discusses the unique decomposition of generators of completely positive dynamical semigroups in infinite dimensions. The main result is that for any separable complex Hilbert space, any trace-class operator B that does not have a purely imaginary trace, and any generator L of a norm-continuous one-parameter semigroup of completely positive maps, there exists a unique bounded operator K and a unique completely positive map Φ such that L=K(·) + (·)K+ Φ. The paper also introduces a modified version of the Choi formalism and characterizes when this correspondence is injective and surjective. The paper concludes by discussing the challenges and questions that arise when generalizing the results to non-separable Hilbert spaces."
],
"text/plain": [
"<IPython.core.display.Markdown object>"
@ -841,15 +756,15 @@
"updated_response = chat_completion_with_function_execution(\n",
" paper_conversation.conversation_history, functions=arxiv_functions\n",
")\n",
"display(Markdown(updated_response[\"choices\"][0][\"message\"][\"content\"]))\n"
"display(Markdown(updated_response.choices[0].message.content))\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "tua_test",
"display_name": "Python 3",
"language": "python",
"name": "tua_test"
"name": "python3"
},
"language_info": {
"codemirror_mode": {
@ -861,7 +776,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.11"
"version": "3.12.1"
}
},
"nbformat": 4,

@ -33,37 +33,68 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 1,
"id": "80e71f33",
"metadata": {
"pycharm": {
"is_executing": true
}
},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: scipy in /usr/local/lib/python3.11/site-packages (1.12.0)\n",
"Requirement already satisfied: numpy<1.29.0,>=1.22.4 in /usr/local/lib/python3.11/site-packages (from scipy) (1.26.3)\n",
"Requirement already satisfied: tenacity in /usr/local/lib/python3.11/site-packages (8.2.3)\n",
"Requirement already satisfied: tiktoken in /usr/local/lib/python3.11/site-packages (0.3.3)\n",
"Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.11/site-packages (from tiktoken) (2023.12.25)\n",
"Requirement already satisfied: requests>=2.26.0 in /usr/local/lib/python3.11/site-packages (from tiktoken) (2.31.0)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken) (3.3.2)\n",
"Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken) (3.6)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken) (2.1.0)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/site-packages (from requests>=2.26.0->tiktoken) (2023.11.17)\n",
"Requirement already satisfied: termcolor in /usr/local/lib/python3.11/site-packages (2.4.0)\n",
"Requirement already satisfied: openai in /usr/local/lib/python3.11/site-packages (1.10.0)\n",
"Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.11/site-packages (from openai) (4.2.0)\n",
"Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.11/site-packages (from openai) (1.9.0)\n",
"Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.11/site-packages (from openai) (0.26.0)\n",
"Requirement already satisfied: pydantic<3,>=1.9.0 in /usr/local/lib/python3.11/site-packages (from openai) (2.5.3)\n",
"Requirement already satisfied: sniffio in /usr/local/lib/python3.11/site-packages (from openai) (1.3.0)\n",
"Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.11/site-packages (from openai) (4.66.1)\n",
"Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.11/site-packages (from openai) (4.9.0)\n",
"Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.11/site-packages (from anyio<5,>=3.5.0->openai) (3.6)\n",
"Requirement already satisfied: certifi in /usr/local/lib/python3.11/site-packages (from httpx<1,>=0.23.0->openai) (2023.11.17)\n",
"Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.11/site-packages (from httpx<1,>=0.23.0->openai) (1.0.2)\n",
"Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.11/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.14.0)\n",
"Requirement already satisfied: annotated-types>=0.4.0 in /usr/local/lib/python3.11/site-packages (from pydantic<3,>=1.9.0->openai) (0.6.0)\n",
"Requirement already satisfied: pydantic-core==2.14.6 in /usr/local/lib/python3.11/site-packages (from pydantic<3,>=1.9.0->openai) (2.14.6)\n"
]
}
],
"source": [
"!pip install scipy\n",
"!pip install tenacity\n",
"!pip install tiktoken\n",
"!pip install termcolor \n",
"!pip install openai\n",
"!pip install requests"
"!pip install openai"
]
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"id": "dab872c5",
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"import openai\n",
"import requests\n",
"from openai import OpenAI\n",
"from tenacity import retry, wait_random_exponential, stop_after_attempt\n",
"from termcolor import colored\n",
"from termcolor import colored \n",
"\n",
"GPT_MODEL = \"gpt-3.5-turbo-0613\""
"GPT_MODEL = \"gpt-3.5-turbo-0613\"\n",
"client = OpenAI()"
]
},
{
@ -79,27 +110,19 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"id": "745ceec5",
"metadata": {},
"outputs": [],
"source": [
"@retry(wait=wait_random_exponential(multiplier=1, max=40), stop=stop_after_attempt(3))\n",
"def chat_completion_request(messages, tools=None, tool_choice=None, model=GPT_MODEL):\n",
" headers = {\n",
" \"Content-Type\": \"application/json\",\n",
" \"Authorization\": \"Bearer \" + openai.api_key,\n",
" }\n",
" json_data = {\"model\": model, \"messages\": messages}\n",
" if tools is not None:\n",
" json_data.update({\"tools\": tools})\n",
" if tool_choice is not None:\n",
" json_data.update({\"tool_choice\": tool_choice})\n",
" try:\n",
" response = requests.post(\n",
" \"https://api.openai.com/v1/chat/completions\",\n",
" headers=headers,\n",
" json=json_data,\n",
" response = client.chat.completions.create(\n",
" model=model,\n",
" messages=messages,\n",
" tools=tools,\n",
" tool_choice=tool_choice,\n",
" )\n",
" return response\n",
" except Exception as e:\n",
@ -110,7 +133,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 4,
"id": "c4d1c99f",
"metadata": {},
"outputs": [],
@ -120,7 +143,7 @@
" \"system\": \"red\",\n",
" \"user\": \"green\",\n",
" \"assistant\": \"blue\",\n",
" \"tool\": \"magenta\",\n",
" \"function\": \"magenta\",\n",
" }\n",
" \n",
" for message in messages:\n",
@ -132,7 +155,7 @@
" print(colored(f\"assistant: {message['function_call']}\\n\", role_to_color[message[\"role\"]]))\n",
" elif message[\"role\"] == \"assistant\" and not message.get(\"function_call\"):\n",
" print(colored(f\"assistant: {message['content']}\\n\", role_to_color[message[\"role\"]]))\n",
" elif message[\"role\"] == \"tool\":\n",
" elif message[\"role\"] == \"function\":\n",
" print(colored(f\"function ({message['name']}): {message['content']}\\n\", role_to_color[message[\"role\"]]))\n"
]
},
@ -149,7 +172,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 5,
"id": "d2e25069",
"metadata": {},
"outputs": [],
@ -217,18 +240,17 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 6,
"id": "518d6827",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'role': 'assistant',\n",
" 'content': 'Sure, I can help you with that. Could you please tell me the city and state you are in or the location you want to know the weather for?'}"
"ChatCompletionMessage(content='Sure, I can help you with that. Could you please provide me with your location?', role='assistant', function_call=None, tool_calls=None)"
]
},
"execution_count": 5,
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
@ -240,7 +262,7 @@
"chat_response = chat_completion_request(\n",
" messages, tools=tools\n",
")\n",
"assistant_message = chat_response.json()[\"choices\"][0][\"message\"]\n",
"assistant_message = chat_response.choices[0].message\n",
"messages.append(assistant_message)\n",
"assistant_message\n"
]
@ -256,22 +278,17 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 7,
"id": "23c42a6e",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'role': 'assistant',\n",
" 'content': None,\n",
" 'tool_calls': [{'id': 'call_o7uyztQLeVIoRdjcDkDJY3ni',\n",
" 'type': 'function',\n",
" 'function': {'name': 'get_current_weather',\n",
" 'arguments': '{\\n \"location\": \"Glasgow, Scotland\",\\n \"format\": \"celsius\"\\n}'}}]}"
"ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_qOYhFO7fKaU6wpG2f1XzkDjW', function=Function(arguments='{\\n \"location\": \"Glasgow, Scotland\",\\n \"format\": \"celsius\"\\n}', name='get_current_weather'), type='function')])"
]
},
"execution_count": 6,
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
@ -281,7 +298,7 @@
"chat_response = chat_completion_request(\n",
" messages, tools=tools\n",
")\n",
"assistant_message = chat_response.json()[\"choices\"][0][\"message\"]\n",
"assistant_message = chat_response.choices[0].message\n",
"messages.append(assistant_message)\n",
"assistant_message\n"
]
@ -297,18 +314,17 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 8,
"id": "fa232e54",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'role': 'assistant',\n",
" 'content': 'Sure, I can help you with that. Please let me know the value for x.'}"
"ChatCompletionMessage(content='Sure! Please provide the number of days you would like to know the weather forecast for.', role='assistant', function_call=None, tool_calls=None)"
]
},
"execution_count": 7,
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
@ -320,7 +336,7 @@
"chat_response = chat_completion_request(\n",
" messages, tools=tools\n",
")\n",
"assistant_message = chat_response.json()[\"choices\"][0][\"message\"]\n",
"assistant_message = chat_response.choices[0].message\n",
"messages.append(assistant_message)\n",
"assistant_message\n"
]
@ -336,24 +352,17 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 9,
"id": "c7d8a543",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'index': 0,\n",
" 'message': {'role': 'assistant',\n",
" 'content': None,\n",
" 'tool_calls': [{'id': 'call_drz2YpGPWEMVySzYgsWYY249',\n",
" 'type': 'function',\n",
" 'function': {'name': 'get_n_day_weather_forecast',\n",
" 'arguments': '{\\n \"location\": \"Glasgow, Scotland\",\\n \"format\": \"celsius\",\\n \"num_days\": 5\\n}'}}]},\n",
" 'finish_reason': 'tool_calls'}"
"Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_HwWHsNZsmkZUroPj6glmEgA5', function=Function(arguments='{\\n \"location\": \"Glasgow, Scotland\",\\n \"format\": \"celsius\",\\n \"num_days\": 5\\n}', name='get_n_day_weather_forecast'), type='function')]), internal_metrics=[{'cached_prompt_tokens': 0, 'total_accepted_tokens': 0, 'total_batched_tokens': 269, 'total_predicted_tokens': 0, 'total_rejected_tokens': 0, 'total_tokens_in_completion': 270, 'cached_embeddings_bytes': 0, 'cached_embeddings_n': 0, 'uncached_embeddings_bytes': 0, 'uncached_embeddings_n': 0, 'fetched_embeddings_bytes': 0, 'fetched_embeddings_n': 0, 'n_evictions': 0, 'sampling_steps': 40, 'sampling_steps_with_predictions': 0, 'batcher_ttft': 0.055008649826049805, 'batcher_initial_queue_time': 0.00098419189453125}])"
]
},
"execution_count": 8,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
@ -363,7 +372,7 @@
"chat_response = chat_completion_request(\n",
" messages, tools=tools\n",
")\n",
"chat_response.json()[\"choices\"][0]\n"
"chat_response.choices[0]\n"
]
},
{
@ -386,22 +395,17 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 10,
"id": "559371b7",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'role': 'assistant',\n",
" 'content': None,\n",
" 'tool_calls': [{'id': 'call_jdmoJQ4lqsu4mBWcVBYtt5cU',\n",
" 'type': 'function',\n",
" 'function': {'name': 'get_n_day_weather_forecast',\n",
" 'arguments': '{\\n \"location\": \"Toronto, Canada\",\\n \"format\": \"celsius\",\\n \"num_days\": 1\\n}'}}]}"
"ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_240XQedt4Gi8VZsUwOvFpQfZ', function=Function(arguments='{\\n \"location\": \"Toronto, Canada\",\\n \"format\": \"celsius\",\\n \"num_days\": 1\\n}', name='get_n_day_weather_forecast'), type='function')])"
]
},
"execution_count": 9,
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
@ -414,27 +418,22 @@
"chat_response = chat_completion_request(\n",
" messages, tools=tools, tool_choice={\"type\": \"function\", \"function\": {\"name\": \"get_n_day_weather_forecast\"}}\n",
")\n",
"chat_response.json()[\"choices\"][0][\"message\"]\n"
"chat_response.choices[0].message"
]
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 11,
"id": "a7ab0f58",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'role': 'assistant',\n",
" 'content': None,\n",
" 'tool_calls': [{'id': 'call_RYXaDjxpUCfWmpXU7BZEYVqS',\n",
" 'type': 'function',\n",
" 'function': {'name': 'get_current_weather',\n",
" 'arguments': '{\\n \"location\": \"Toronto, Canada\",\\n \"format\": \"celsius\"\\n}'}}]}"
"ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_lQhrFlzIVPpeYG1QrSv7e3H3', function=Function(arguments='{\\n \"location\": \"Toronto, Canada\",\\n \"format\": \"celsius\"\\n}', name='get_current_weather'), type='function')])"
]
},
"execution_count": 10,
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
@ -447,7 +446,7 @@
"chat_response = chat_completion_request(\n",
" messages, tools=tools\n",
")\n",
"chat_response.json()[\"choices\"][0][\"message\"]\n"
"chat_response.choices[0].message"
]
},
{
@ -461,18 +460,17 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 12,
"id": "acfe54e6",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'role': 'assistant',\n",
" 'content': '{ \"location\": \"Toronto, Canada\", \"format\": \"celsius\" }'}"
"ChatCompletionMessage(content='{\\n \"location\": \"Toronto, Canada\",\\n \"format\": \"celsius\"\\n}', role='assistant', function_call=None, tool_calls=None)"
]
},
"execution_count": 11,
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
@ -484,11 +482,12 @@
"chat_response = chat_completion_request(\n",
" messages, tools=tools, tool_choice=\"none\"\n",
")\n",
"chat_response.json()[\"choices\"][0][\"message\"]\n"
"chat_response.choices[0].message\n"
]
},
{
"cell_type": "markdown",
"id": "b616353b",
"metadata": {},
"source": [
"### Parallel Function Calling\n",
@ -498,23 +497,18 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 13,
"id": "380eeb68",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'id': 'call_fLsKR5vGllhbWxvpqsDT3jBj',\n",
" 'type': 'function',\n",
" 'function': {'name': 'get_n_day_weather_forecast',\n",
" 'arguments': '{\"location\": \"San Francisco, CA\", \"format\": \"celsius\", \"num_days\": 4}'}},\n",
" {'id': 'call_CchlsGE8OE03QmeyFbg7pkDz',\n",
" 'type': 'function',\n",
" 'function': {'name': 'get_n_day_weather_forecast',\n",
" 'arguments': '{\"location\": \"Glasgow\", \"format\": \"celsius\", \"num_days\": 4}'}}]"
"[ChatCompletionMessageToolCall(id='call_q8k4geh0uGPRtIfOXYPB0yM8', function=Function(arguments='{\"location\": \"San Francisco, CA\", \"format\": \"celsius\", \"num_days\": 4}', name='get_n_day_weather_forecast'), type='function'),\n",
" ChatCompletionMessageToolCall(id='call_Hdl7Py7aLswCBPptrD4y5BD3', function=Function(arguments='{\"location\": \"Glasgow\", \"format\": \"celsius\", \"num_days\": 4}', name='get_n_day_weather_forecast'), type='function')]"
]
},
"execution_count": 12,
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
@ -527,8 +521,7 @@
" messages, tools=tools, model='gpt-3.5-turbo-1106'\n",
")\n",
"\n",
"chat_response.json()\n",
"assistant_message = chat_response.json()[\"choices\"][0][\"message\"]['tool_calls']\n",
"assistant_message = chat_response.choices[0].message.tool_calls\n",
"assistant_message"
]
},
@ -558,7 +551,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 14,
"id": "30f6b60e",
"metadata": {},
"outputs": [
@ -579,7 +572,7 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 15,
"id": "abec0214",
"metadata": {},
"outputs": [],
@ -622,7 +615,7 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 16,
"id": "0c0104cd",
"metadata": {},
"outputs": [],
@ -647,7 +640,7 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 17,
"id": "0258813a",
"metadata": {},
"outputs": [],
@ -691,7 +684,7 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": 18,
"id": "65585e74",
"metadata": {},
"outputs": [],
@ -705,17 +698,17 @@
" return results\n",
"\n",
"def execute_function_call(message):\n",
" if message[\"tool_calls\"][0][\"function\"][\"name\"] == \"ask_database\":\n",
" query = json.loads(message[\"tool_calls\"][0][\"function\"][\"arguments\"])[\"query\"]\n",
" if message.tool_calls[0].function.name == \"ask_database\":\n",
" query = json.loads(message.tool_calls[0].function.arguments)[\"query\"]\n",
" results = ask_database(conn, query)\n",
" else:\n",
" results = f\"Error: function {message['tool_calls'][0]['function']['name']} does not exist\"\n",
" results = f\"Error: function {message.tool_calls[0].function.name} does not exist\"\n",
" return results"
]
},
{
"cell_type": "code",
"execution_count": 18,
"execution_count": 19,
"id": "38c55083",
"metadata": {},
"outputs": [
@ -727,7 +720,7 @@
"\u001b[0m\n",
"\u001b[32muser: Hi, who are the top 5 artists by number of tracks?\n",
"\u001b[0m\n",
"\u001b[34massistant: {'name': 'ask_database', 'arguments': '{\\n \"query\": \"SELECT Artist.Name, COUNT(Track.TrackId) AS TrackCount FROM Artist JOIN Album ON Artist.ArtistId = Album.ArtistId JOIN Track ON Album.AlbumId = Track.AlbumId GROUP BY Artist.Name ORDER BY TrackCount DESC LIMIT 5\"\\n}'}\n",
"\u001b[34massistant: Function(arguments='{\\n \"query\": \"SELECT artist.Name, COUNT(track.TrackId) AS num_tracks FROM artist JOIN album ON artist.ArtistId = album.ArtistId JOIN track ON album.AlbumId = track.AlbumId GROUP BY artist.ArtistId ORDER BY num_tracks DESC LIMIT 5\"\\n}', name='ask_database')\n",
"\u001b[0m\n",
"\u001b[35mfunction (ask_database): [('Iron Maiden', 213), ('U2', 135), ('Led Zeppelin', 114), ('Metallica', 112), ('Lost', 92)]\n",
"\u001b[0m\n"
@ -739,18 +732,18 @@
"messages.append({\"role\": \"system\", \"content\": \"Answer user questions by generating SQL queries against the Chinook Music Database.\"})\n",
"messages.append({\"role\": \"user\", \"content\": \"Hi, who are the top 5 artists by number of tracks?\"})\n",
"chat_response = chat_completion_request(messages, tools)\n",
"assistant_message = chat_response.json()[\"choices\"][0][\"message\"]\n",
"assistant_message['content'] = str(assistant_message[\"tool_calls\"][0][\"function\"])\n",
"messages.append(assistant_message)\n",
"if assistant_message.get(\"tool_calls\"):\n",
"assistant_message = chat_response.choices[0].message\n",
"assistant_message.content = str(assistant_message.tool_calls[0].function)\n",
"messages.append({\"role\": assistant_message.role, \"content\": assistant_message.content})\n",
"if assistant_message.tool_calls:\n",
" results = execute_function_call(assistant_message)\n",
" messages.append({\"role\": \"tool\", \"tool_call_id\": assistant_message[\"tool_calls\"][0]['id'], \"name\": assistant_message[\"tool_calls\"][0][\"function\"][\"name\"], \"content\": results})\n",
" messages.append({\"role\": \"function\", \"tool_call_id\": assistant_message.tool_calls[0].id, \"name\": assistant_message.tool_calls[0].function.name, \"content\": results})\n",
"pretty_print_conversation(messages)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 20,
"id": "710481dc",
"metadata": {
"scrolled": true
@ -764,13 +757,13 @@
"\u001b[0m\n",
"\u001b[32muser: Hi, who are the top 5 artists by number of tracks?\n",
"\u001b[0m\n",
"\u001b[34massistant: {'name': 'ask_database', 'arguments': '{\\n \"query\": \"SELECT Artist.Name, COUNT(Track.TrackId) AS TrackCount FROM Artist JOIN Album ON Artist.ArtistId = Album.ArtistId JOIN Track ON Album.AlbumId = Track.AlbumId GROUP BY Artist.Name ORDER BY TrackCount DESC LIMIT 5\"\\n}'}\n",
"\u001b[34massistant: Function(arguments='{\\n \"query\": \"SELECT artist.Name, COUNT(track.TrackId) AS num_tracks FROM artist JOIN album ON artist.ArtistId = album.ArtistId JOIN track ON album.AlbumId = track.AlbumId GROUP BY artist.ArtistId ORDER BY num_tracks DESC LIMIT 5\"\\n}', name='ask_database')\n",
"\u001b[0m\n",
"\u001b[35mfunction (ask_database): [('Iron Maiden', 213), ('U2', 135), ('Led Zeppelin', 114), ('Metallica', 112), ('Lost', 92)]\n",
"\u001b[0m\n",
"\u001b[32muser: What is the name of the album with the most tracks?\n",
"\u001b[0m\n",
"\u001b[34massistant: {'name': 'ask_database', 'arguments': '{\\n \"query\": \"SELECT Album.Title, COUNT(Track.TrackId) AS TrackCount FROM Album JOIN Track ON Album.AlbumId = Track.AlbumId GROUP BY Album.Title ORDER BY TrackCount DESC LIMIT 1\"\\n}'}\n",
"\u001b[34massistant: Function(arguments='{\\n \"query\": \"SELECT album.Title, COUNT(track.TrackId) AS num_tracks FROM album JOIN track ON album.AlbumId = track.AlbumId GROUP BY album.AlbumId ORDER BY num_tracks DESC LIMIT 1\"\\n}', name='ask_database')\n",
"\u001b[0m\n",
"\u001b[35mfunction (ask_database): [('Greatest Hits', 57)]\n",
"\u001b[0m\n"
@ -780,12 +773,12 @@
"source": [
"messages.append({\"role\": \"user\", \"content\": \"What is the name of the album with the most tracks?\"})\n",
"chat_response = chat_completion_request(messages, tools)\n",
"assistant_message = chat_response.json()[\"choices\"][0][\"message\"]\n",
"assistant_message['content'] = str(assistant_message[\"tool_calls\"][0][\"function\"])\n",
"messages.append(assistant_message)\n",
"if assistant_message.get(\"tool_calls\"):\n",
"assistant_message = chat_response.choices[0].message\n",
"assistant_message.content = str(assistant_message.tool_calls[0].function)\n",
"messages.append({\"role\": assistant_message.role, \"content\": assistant_message.content})\n",
"if assistant_message.tool_calls:\n",
" results = execute_function_call(assistant_message)\n",
" messages.append({\"role\": \"tool\", \"tool_call_id\": assistant_message[\"tool_calls\"][0]['id'], \"name\": assistant_message[\"tool_calls\"][0][\"function\"][\"name\"], \"content\": results})\n",
" messages.append({\"role\": \"function\", \"tool_call_id\": assistant_message.tool_calls[0].id, \"name\": assistant_message.tool_calls[0].function.name, \"content\": results})\n",
"pretty_print_conversation(messages)"
]
},
@ -799,6 +792,12 @@
"\n",
"See our other [notebook](How_to_call_functions_for_knowledge_retrieval.ipynb) that demonstrates how to use the Chat Completions API and functions for knowledge retrieval to interact conversationally with a knowledge base."
]
},
{
"cell_type": "markdown",
"id": "ec721d07",
"metadata": {},
"source": []
}
],
"metadata": {
@ -817,7 +816,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
"version": "3.12.1"
}
},
"nbformat": 4,

@ -22,7 +22,7 @@
"\n",
"| Encoding name | OpenAI models |\n",
"|-------------------------|-----------------------------------------------------|\n",
"| `cl100k_base` | `gpt-4`, `gpt-3.5-turbo`, `text-embedding-ada-002` |\n",
"| `cl100k_base` | `gpt-4`, `gpt-3.5-turbo`, `text-embedding-ada-002`, `text-embedding-3-small`, `text-embedding-3-large` |\n",
"| `p50k_base` | Codex models, `text-davinci-002`, `text-davinci-003`|\n",
"| `r50k_base` (or `gpt2`) | GPT-3 models like `davinci` |\n",
"\n",
@ -89,7 +89,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@ -433,7 +433,7 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@ -480,7 +480,7 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 4,
"metadata": {},
"outputs": [
{
@ -519,7 +519,10 @@
"source": [
"# let's verify the function above matches the OpenAI API response\n",
"\n",
"import openai\n",
"from openai import OpenAI\n",
"import os\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n",
"\n",
"example_messages = [\n",
" {\n",
@ -564,15 +567,20 @@
" # example token count from the function defined above\n",
" print(f\"{num_tokens_from_messages(example_messages, model)} prompt tokens counted by num_tokens_from_messages().\")\n",
" # example token count from the OpenAI API\n",
" response = openai.ChatCompletion.create(\n",
" model=model,\n",
" messages=example_messages,\n",
" temperature=0,\n",
" max_tokens=1, # we're only counting input tokens here, so let's not waste tokens on the output\n",
" )\n",
" print(f'{response[\"usage\"][\"prompt_tokens\"]} prompt tokens counted by the OpenAI API.')\n",
" response = client.chat.completions.create(model=model,\n",
" messages=example_messages,\n",
" temperature=0,\n",
" max_tokens=1)\n",
" print(f'{response.usage.prompt_tokens} prompt tokens counted by the OpenAI API.')\n",
" print()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
@ -591,7 +599,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
"version": "3.11.5"
},
"vscode": {
"interpreter": {

@ -42,7 +42,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 12,
"id": "6e1f4403-37e1-4115-a215-12fd7daa1eb6",
"metadata": {},
"outputs": [],
@ -53,7 +53,7 @@
"import pandas as pd\n",
"from pprint import pprint\n",
"\n",
"OPENAI_API_KEY = os.getenv(\"OPENAI_API_KEY\", \"\")"
"client = openai.OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))"
]
},
{
@ -67,7 +67,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"id": "f57ebc23-14b7-47f9-90b8-1d791ccfc9bc",
"metadata": {},
"outputs": [
@ -151,22 +151,22 @@
"</div>"
],
"text/plain": [
" title ingredients \\\n",
"0 No-Bake Nut Cookies [\"1 c. firmly packed brown sugar\", \"1/2 c. eva... \n",
" title ingredients \n",
"0 No-Bake Nut Cookies [\"1 c. firmly packed brown sugar\", \"1/2 c. eva... \\\n",
"1 Jewell Ball'S Chicken [\"1 small jar chipped beef, cut up\", \"4 boned ... \n",
"2 Creamy Corn [\"2 (16 oz.) pkg. frozen corn\", \"1 (8 oz.) pkg... \n",
"3 Chicken Funny [\"1 large whole chicken\", \"2 (10 1/2 oz.) cans... \n",
"4 Reeses Cups(Candy) [\"1 c. peanut butter\", \"3/4 c. graham cracker ... \n",
"\n",
" directions \\\n",
"0 [\"In a heavy 2-quart saucepan, mix brown sugar... \n",
" directions \n",
"0 [\"In a heavy 2-quart saucepan, mix brown sugar... \\\n",
"1 [\"Place chipped beef on bottom of baking dish.... \n",
"2 [\"In a slow cooker, combine all ingredients. C... \n",
"3 [\"Boil and debone chicken.\", \"Put bite size pi... \n",
"4 [\"Combine first four ingredients and press in ... \n",
"\n",
" link source \\\n",
"0 www.cookbooks.com/Recipe-Details.aspx?id=44874 www.cookbooks.com \n",
" link source \n",
"0 www.cookbooks.com/Recipe-Details.aspx?id=44874 www.cookbooks.com \\\n",
"1 www.cookbooks.com/Recipe-Details.aspx?id=699419 www.cookbooks.com \n",
"2 www.cookbooks.com/Recipe-Details.aspx?id=10570 www.cookbooks.com \n",
"3 www.cookbooks.com/Recipe-Details.aspx?id=897570 www.cookbooks.com \n",
@ -405,7 +405,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 13,
"id": "69462d9e-e6bd-49b9-a064-9eae4ea5b7a8",
"metadata": {},
"outputs": [
@ -413,21 +413,24 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Training file ID: file-jcdvNl27iuBMZfwi4q30IIka\n",
"Validation file ID: file-O144OIHkZ1xjB32ednBmbOXP\n"
"Training file ID: file-PVkEstNM2WWd1OQe3Hp3tC5E\n",
"Validation file ID: file-WSdTwLYrKxNhKi1WWGjxXi87\n"
]
}
],
"source": [
"training_response = openai.File.create(\n",
" file=open(training_file_name, \"rb\"), purpose=\"fine-tune\"\n",
")\n",
"training_file_id = training_response[\"id\"]\n",
"with open(training_file_name, \"rb\") as training_fd:\n",
" training_response = client.files.create(\n",
" file=training_fd, purpose=\"fine-tune\"\n",
" )\n",
"\n",
"validation_response = openai.File.create(\n",
" file=open(validation_file_name, \"rb\"), purpose=\"fine-tune\"\n",
")\n",
"validation_file_id = validation_response[\"id\"]\n",
"training_file_id = training_response.id\n",
"\n",
"with open(validation_file_name, \"rb\") as validation_fd:\n",
" validation_response = client.files.create(\n",
" file=validation_fd, purpose=\"fine-tune\"\n",
" )\n",
"validation_file_id = validation_response.id\n",
"\n",
"print(\"Training file ID:\", training_file_id)\n",
"print(\"Validation file ID:\", validation_file_id)"
@ -447,7 +450,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 14,
"id": "05541ceb-5628-447e-962d-7e57c112439c",
"metadata": {},
"outputs": [
@ -455,23 +458,23 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Job ID: ftjob-9xVzrp0Oem9rWj2UFWDcFLqT\n",
"Status: created\n"
"Job ID: ftjob-bIVrnhnZEEizSP7rqWsRwv2R\n",
"Status: validating_files\n"
]
}
],
"source": [
"response = openai.FineTuningJob.create(\n",
"response = client.fine_tuning.jobs.create(\n",
" training_file=training_file_id,\n",
" validation_file=validation_file_id,\n",
" model=\"gpt-3.5-turbo\",\n",
" suffix=\"recipe-ner\",\n",
")\n",
"\n",
"job_id = response[\"id\"]\n",
"job_id = response.id\n",
"\n",
"print(\"Job ID:\", response[\"id\"])\n",
"print(\"Status:\", response[\"status\"])"
"print(\"Job ID:\", response.id)\n",
"print(\"Status:\", response.status)"
]
},
{
@ -488,7 +491,7 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 21,
"id": "d7392f48",
"metadata": {},
"outputs": [
@ -496,18 +499,18 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Job ID: ftjob-9xVzrp0Oem9rWj2UFWDcFLqT\n",
"Job ID: ftjob-bIVrnhnZEEizSP7rqWsRwv2R\n",
"Status: running\n",
"Trained Tokens: None\n"
]
}
],
"source": [
"response = openai.FineTuningJob.retrieve(job_id)\n",
"response = client.fine_tuning.jobs.retrieve(job_id)\n",
"\n",
"print(\"Job ID:\", response[\"id\"])\n",
"print(\"Status:\", response[\"status\"])\n",
"print(\"Trained Tokens:\", response[\"trained_tokens\"])\n"
"print(\"Job ID:\", response.id)\n",
"print(\"Status:\", response.status)\n",
"print(\"Trained Tokens:\", response.trained_tokens)\n"
]
},
{
@ -520,7 +523,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 47,
"id": "08cace28",
"metadata": {},
"outputs": [
@ -528,51 +531,37 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Created fine-tune: ftjob-9xVzrp0Oem9rWj2UFWDcFLqT\n",
"Fine tuning job started\n",
"Step 10: training loss=2.41\n",
"Step 20: training loss=0.06\n",
"Step 30: training loss=0.38\n",
"Step 40: training loss=0.09\n",
"Step 50: training loss=0.19\n",
"Step 60: training loss=0.10\n",
"Step 70: training loss=0.00\n",
"Step 80: training loss=0.01\n",
"Step 90: training loss=0.72\n",
"Step 100: training loss=0.13\n",
"Step 110: training loss=0.15\n",
"Step 120: training loss=0.00\n",
"Step 130: training loss=0.47\n",
"Step 140: training loss=0.00\n",
"Step 150: training loss=0.10\n",
"Step 160: training loss=0.06\n",
"Step 170: training loss=0.03\n",
"Step 180: training loss=0.08\n",
"Step 190: training loss=0.04\n",
"Step 200: training loss=0.01\n",
"Step 210: training loss=0.03\n",
"Step 220: training loss=0.00\n",
"Step 230: training loss=0.08\n",
"Step 240: training loss=0.00\n",
"Step 250: training loss=0.00\n",
"Step 260: training loss=0.00\n",
"Step 270: training loss=0.00\n",
"Step 280: training loss=0.00\n",
"Step 290: training loss=0.00\n",
"Step 300: training loss=0.60\n",
"New fine-tuned model created: ft:gpt-3.5-turbo-0613:openai:recipe-ner:7qTvyJ81\n",
"Fine-tuning job successfully completed\n"
"Step 131/303: training loss=0.25, validation loss=0.37\n",
"Step 141/303: training loss=0.00, validation loss=0.19\n",
"Step 151/303: training loss=0.00, validation loss=0.11\n",
"Step 161/303: training loss=0.00, validation loss=0.06\n",
"Step 171/303: training loss=0.10, validation loss=0.00\n",
"Step 181/303: training loss=0.00, validation loss=0.38\n",
"Step 191/303: training loss=0.00, validation loss=0.15\n",
"Step 201/303: training loss=0.06, validation loss=0.64\n",
"Step 211/303: training loss=0.00, validation loss=0.04\n",
"Step 221/303: training loss=0.59, validation loss=0.85\n",
"Step 231/303: training loss=0.00, validation loss=0.00\n",
"Step 241/303: training loss=0.04, validation loss=0.42\n",
"Step 251/303: training loss=0.00, validation loss=0.14\n",
"Step 261/303: training loss=0.00, validation loss=0.00\n",
"Step 271/303: training loss=0.15, validation loss=0.50\n",
"Step 281/303: training loss=0.00, validation loss=0.72\n",
"Step 291/303: training loss=0.08, validation loss=0.16\n",
"Step 301/303: training loss=0.00, validation loss=1.76\n",
"New fine-tuned model created: ft:gpt-3.5-turbo-0613:personal:recipe-ner:8PjmcwDH\n",
"The job has successfully completed\n"
]
}
],
"source": [
"response = openai.FineTuningJob.list_events(id=job_id, limit=50)\n",
"response = client.fine_tuning.jobs.list_events(job_id)\n",
"\n",
"events = response[\"data\"]\n",
"events = response.data\n",
"events.reverse()\n",
"\n",
"for event in events:\n",
" print(event[\"message\"])"
" print(event.message)"
]
},
{
@ -585,7 +574,7 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 48,
"id": "40b28c26",
"metadata": {},
"outputs": [
@ -593,13 +582,13 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Fine-tuned model ID: ft:gpt-3.5-turbo-0613:openai:recipe-ner:7qTvyJ81\n"
"Fine-tuned model ID: ft:gpt-3.5-turbo-0613:personal:recipe-ner:8PjmcwDH\n"
]
}
],
"source": [
"response = openai.FineTuningJob.retrieve(job_id)\n",
"fine_tuned_model_id = response[\"fine_tuned_model\"]\n",
"response = client.fine_tuning.jobs.retrieve(job_id)\n",
"fine_tuned_model_id = response.fine_tuned_model\n",
"\n",
"if fine_tuned_model_id is None: \n",
" raise RuntimeError(\"Fine-tuned model ID not found. Your job has likely not been completed yet.\")\n",
@ -625,7 +614,7 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 49,
"id": "1c7de631-b68f-4eff-9ae7-051641579c2b",
"metadata": {},
"outputs": [
@ -636,11 +625,12 @@
"[{'content': 'You are a helpful recipe assistant. You are to extract the '\n",
" 'generic ingredients from each of the recipes provided.',\n",
" 'role': 'system'},\n",
" {'content': 'Title: Pancakes\\n'\n",
" {'content': 'Title: Beef Brisket\\n'\n",
" '\\n'\n",
" 'Ingredients: [\"1 c. flour\", \"1 tsp. soda\", \"1 tsp. salt\", \"1 '\n",
" 'Tbsp. sugar\", \"1 egg\", \"3 Tbsp. margarine, melted\", \"1 c. '\n",
" 'buttermilk\"]\\n'\n",
" 'Ingredients: [\"4 lb. beef brisket\", \"1 c. catsup\", \"1 c. water\", '\n",
" '\"1/2 onion, minced\", \"2 Tbsp. cider vinegar\", \"1 Tbsp. prepared '\n",
" 'horseradish\", \"1 Tbsp. prepared mustard\", \"1 tsp. salt\", \"1/2 '\n",
" 'tsp. pepper\"]\\n'\n",
" '\\n'\n",
" 'Generic ingredients: ',\n",
" 'role': 'user'}]\n"
@ -660,7 +650,7 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 50,
"id": "1a1d2589",
"metadata": {},
"outputs": [
@ -668,15 +658,15 @@
"name": "stdout",
"output_type": "stream",
"text": [
"[\"flour\", \"soda\", \"salt\", \"sugar\", \"egg\", \"margarine\", \"buttermilk\"]\n"
"[\"beef brisket\", \"catsup\", \"water\", \"onion\", \"cider vinegar\", \"horseradish\", \"mustard\", \"salt\", \"pepper\"]\n"
]
}
],
"source": [
"response = openai.ChatCompletion.create(\n",
"response = client.chat.completions.create(\n",
" model=fine_tuned_model_id, messages=test_messages, temperature=0, max_tokens=500\n",
")\n",
"print(response[\"choices\"][0][\"message\"][\"content\"])"
"print(response.choices[0].message.content)"
]
},
{
@ -706,7 +696,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
"version": "3.11.3"
}
},
"nbformat": 4,

@ -36,12 +36,15 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"# import the OpenAI Python library for calling the OpenAI API\n",
"import openai\n"
"from openai import OpenAI\n",
"import os\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))"
]
},
{
@ -49,17 +52,37 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. An example chat API call\n",
"## 2. An example chat completion API call\n",
"\n",
"A chat API call has two required inputs:\n",
"- `model`: the name of the model you want to use (e.g., `gpt-3.5-turbo`, `gpt-4`, `gpt-3.5-turbo-0613`, `gpt-3.5-turbo-16k-0613`)\n",
"A chat completion API call parameters,\n",
"**Required**\n",
"- `model`: the name of the model you want to use (e.g., `gpt-3.5-turbo`, `gpt-4`, `gpt-3.5-turbo-16k-1106`)\n",
"- `messages`: a list of message objects, where each object has two required fields:\n",
" - `role`: the role of the messenger (either `system`, `user`, or `assistant`)\n",
" - `role`: the role of the messenger (either `system`, `user`, `assistant` or `tool`)\n",
" - `content`: the content of the message (e.g., `Write me a beautiful poem`)\n",
"\n",
"Messages can also contain an optional `name` field, which give the messenger a name. E.g., `example-user`, `Alice`, `BlackbeardBot`. Names may not contain spaces.\n",
"\n",
"As of June 2023, you can also optionally submit a list of `functions` that tell GPT whether it can generate JSON to feed into a function. For details, see the [documentation](https://platform.openai.com/docs/guides/gpt/function-calling), [API reference](https://platform.openai.com/docs/api-reference/chat), or the Cookbook guide [How to call functions with chat models](How_to_call_functions_with_chat_models.ipynb).\n",
"**Optional**\n",
"- `frequency_penalty`: Penalizes tokens based on their frequency, reducing repetition.\n",
"- `logit_bias`: Modifies likelihood of specified tokens with bias values.\n",
"- `logprobs`: Returns log probabilities of output tokens if true.\n",
"- `top_logprobs`: Specifies the number of most likely tokens to return at each position.\n",
"- `max_tokens`: Sets the maximum number of generated tokens in chat completion.\n",
"- `n`: Generates a specified number of chat completion choices for each input.\n",
"- `presence_penalty`: Penalizes new tokens based on their presence in the text.\n",
"- `response_format`: Specifies the output format, e.g., JSON mode.\n",
"- `seed`: Ensures deterministic sampling with a specified seed.\n",
"- `stop`: Specifies up to 4 sequences where the API should stop generating tokens.\n",
"- `stream`: Sends partial message deltas as tokens become available.\n",
"- `temperature`: Sets the sampling temperature between 0 and 2.\n",
"- `top_p`: Uses nucleus sampling; considers tokens with top_p probability mass.\n",
"- `tools`: Lists functions the model may call.\n",
"- `tool_choice`: Controls the model's function calls (none/auto/function).\n",
"- `user`: Unique identifier for end-user monitoring and abuse detection.\n",
"\n",
"\n",
"As of January 2024, you can also optionally submit a list of `functions` that tell GPT whether it can generate JSON to feed into a function. For details, see the [documentation](https://platform.openai.com/docs/guides/function-calling), [API reference](https://platform.openai.com/docs/api-reference/chat), or the Cookbook guide [How to call functions with chat models](How_to_call_functions_with_chat_models.ipynb).\n",
"\n",
"Typically, a conversation will start with a system message that tells the assistant how to behave, followed by alternating user and assistant messages, but you are not required to follow this format.\n",
"\n",
@ -68,44 +91,13 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<OpenAIObject chat.completion id=chatcmpl-7UkgnSDzlevZxiy0YjZcLYdUMz5yZ at 0x118e394f0> JSON: {\n",
" \"id\": \"chatcmpl-7UkgnSDzlevZxiy0YjZcLYdUMz5yZ\",\n",
" \"object\": \"chat.completion\",\n",
" \"created\": 1687563669,\n",
" \"model\": \"gpt-3.5-turbo-0301\",\n",
" \"choices\": [\n",
" {\n",
" \"index\": 0,\n",
" \"message\": {\n",
" \"role\": \"assistant\",\n",
" \"content\": \"Orange who?\"\n",
" },\n",
" \"finish_reason\": \"stop\"\n",
" }\n",
" ],\n",
" \"usage\": {\n",
" \"prompt_tokens\": 39,\n",
" \"completion_tokens\": 3,\n",
" \"total_tokens\": 42\n",
" }\n",
"}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"outputs": [],
"source": [
"# Example OpenAI Python library request\n",
"MODEL = \"gpt-3.5-turbo\"\n",
"response = openai.ChatCompletion.create(\n",
"response = client.chat.completions.create(\n",
" model=MODEL,\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n",
@ -114,9 +106,48 @@
" {\"role\": \"user\", \"content\": \"Orange.\"},\n",
" ],\n",
" temperature=0,\n",
")\n",
"\n",
"response\n"
")\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{\n",
" \"id\": \"chatcmpl-8dee9DuEFcg2QILtT2a6EBXZnpirM\",\n",
" \"choices\": [\n",
" {\n",
" \"finish_reason\": \"stop\",\n",
" \"index\": 0,\n",
" \"logprobs\": null,\n",
" \"message\": {\n",
" \"content\": \"Orange who?\",\n",
" \"role\": \"assistant\",\n",
" \"function_call\": null,\n",
" \"tool_calls\": null\n",
" }\n",
" }\n",
" ],\n",
" \"created\": 1704461729,\n",
" \"model\": \"gpt-3.5-turbo-0613\",\n",
" \"object\": \"chat.completion\",\n",
" \"system_fingerprint\": null,\n",
" \"usage\": {\n",
" \"completion_tokens\": 3,\n",
" \"prompt_tokens\": 35,\n",
" \"total_tokens\": 38\n",
" }\n",
"}\n"
]
}
],
"source": [
"print(json.dumps(json.loads(response.model_dump_json()), indent=4))"
]
},
{
@ -126,14 +157,19 @@
"source": [
"As you can see, the response object has a few fields:\n",
"- `id`: the ID of the request\n",
"- `object`: the type of object returned (e.g., `chat.completion`)\n",
"- `created`: the timestamp of the request\n",
"- `model`: the full name of the model used to generate the response\n",
"- `usage`: the number of tokens used to generate the replies, counting prompt, completion, and total\n",
"- `choices`: a list of completion objects (only one, unless you set `n` greater than 1)\n",
" - `message`: the message object generated by the model, with `role` and `content`\n",
" - `finish_reason`: the reason the model stopped generating text (either `stop`, or `length` if `max_tokens` limit was reached)\n",
" - `index`: the index of the completion in the list of choices"
" - `index`: The index of the choice in the list of choices.\n",
" - `logprobs`: Log probability information for the choice.\n",
" - `message`: the message object generated by the model\n",
" - `content`: content of message\n",
" - `role`: The role of the author of this message.\n",
" - `tool_calls`: The tool calls generated by the model, such as function calls. if the tools is given\n",
"- `created`: the timestamp of the request\n",
"- `model`: the full name of the model used to generate the response\n",
"- `object`: the type of object returned (e.g., `chat.completion`)\n",
"- `system_fingerprint`: This fingerprint represents the backend configuration that the model runs with.\n",
"- `usage`: the number of tokens used to generate the replies, counting prompt, completion, and total"
]
},
{
@ -146,7 +182,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 9,
"metadata": {},
"outputs": [
{
@ -155,13 +191,13 @@
"'Orange who?'"
]
},
"execution_count": 4,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"response['choices'][0]['message']['content']\n"
"response.choices[0].message.content\n"
]
},
{
@ -176,20 +212,36 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ahoy matey! Asynchronous programming be like havin' a crew o' pirates workin' on different tasks at the same time. Ye see, instead o' waitin' for one task to be completed before startin' the next, ye can assign tasks to yer crew and let 'em work on 'em simultaneously. This way, ye can get more done in less time and keep yer ship sailin' smoothly. It be like havin' a bunch o' pirates rowin' the ship at different speeds, but still gettin' us to our destination. Arrr!\n"
"Arr, me matey! Let me tell ye a tale of asynchronous programming, in the style of the fearsome pirate Blackbeard!\n",
"\n",
"Picture this, me hearties. In the vast ocean of programming, there be times when ye need to perform multiple tasks at once. But fear not, for asynchronous programming be here to save the day!\n",
"\n",
"Ye see, in traditional programming, ye be waitin' for one task to be done before movin' on to the next. But with asynchronous programming, ye can be takin' care of multiple tasks at the same time, just like a pirate multitaskin' on the high seas!\n",
"\n",
"Instead of waitin' for a task to be completed, ye can be sendin' it off on its own journey, while ye move on to the next task. It be like havin' a crew of trusty sailors, each takin' care of their own duties, without waitin' for the others.\n",
"\n",
"Now, ye may be wonderin', how does this sorcery work? Well, me matey, it be all about callbacks and promises. When ye be sendin' off a task, ye be attachin' a callback function to it. This be like leavin' a message in a bottle, tellin' the task what to do when it be finished.\n",
"\n",
"While the task be sailin' on its own, ye can be movin' on to the next task, without wastin' any precious time. And when the first task be done, it be sendin' a signal back to ye, lettin' ye know it be finished. Then ye can be takin' care of the callback function, like openin' the bottle and readin' the message inside.\n",
"\n",
"But wait, there be more! With promises, ye can be makin' even fancier arrangements. Instead of callbacks, ye be makin' a promise that the task will be completed. It be like a contract between ye and the task, swearin' that it will be done.\n",
"\n",
"Ye can be attachin' multiple promises to a task, promisin' different outcomes. And when the task be finished, it be fulfillin' the promises, lettin' ye know it be done. Then ye can be handlin' the fulfillments, like collectin' the rewards of yer pirate adventures!\n",
"\n",
"So, me hearties, that be the tale of asynchronous programming, told in the style of the fearsome pirate Blackbeard! With callbacks and promises, ye can be takin' care of multiple tasks at once, just like a pirate conquerin' the seven seas!\n"
]
}
],
"source": [
"# example with a system message\n",
"response = openai.ChatCompletion.create(\n",
"response = client.chat.completions.create(\n",
" model=MODEL,\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n",
@ -198,31 +250,43 @@
" temperature=0,\n",
")\n",
"\n",
"print(response['choices'][0]['message']['content'])\n"
"print(response.choices[0].message.content)\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ahoy mateys! Let me tell ye about asynchronous programming, arrr! It be like havin' a crew of sailors workin' on different tasks at the same time, without waitin' for each other to finish. Ye see, in traditional programming, ye have to wait for one task to be completed before movin' on to the next. But with asynchronous programming, ye can start multiple tasks at once and let them run in the background while ye focus on other things.\n",
"Arr, me hearties! Gather 'round and listen up, for I be tellin' ye about the mysterious art of asynchronous programming, in the style of the fearsome pirate Blackbeard!\n",
"\n",
"It be like havin' a lookout keepin' watch for enemy ships while the rest of the crew be busy with their own tasks. They don't have to stop what they're doin' to keep an eye out, because the lookout be doin' it for them. And when the lookout spots an enemy ship, they can alert the crew and everyone can work together to defend the ship.\n",
"Now, ye see, in the world of programming, there be times when we need to perform tasks that take a mighty long time to complete. These tasks might involve fetchin' data from the depths of the internet, or performin' complex calculations that would make even Davy Jones scratch his head.\n",
"\n",
"In the same way, asynchronous programming allows different parts of yer code to work together without gettin' in each other's way. It be especially useful for tasks that take a long time to complete, like loadin' large files or connectin' to a server. Instead of makin' yer program wait for these tasks to finish, ye can let them run in the background while yer program continues to do other things.\n",
"In the olden days, we pirates used to wait patiently for each task to finish afore movin' on to the next one. But that be a waste of precious time, me hearties! We be pirates, always lookin' for ways to be more efficient and plunder more booty!\n",
"\n",
"So there ye have it, me hearties! Asynchronous programming be like havin' a crew of sailors workin' together without gettin' in each other's way. It be a powerful tool for any programmer, and one that can help ye sail the seas of code with ease!\n"
"That be where asynchronous programming comes in, me mateys. It be a way to tackle multiple tasks at once, without waitin' for each one to finish afore movin' on. It be like havin' a crew of scallywags workin' on different tasks simultaneously, while ye be overseein' the whole operation.\n",
"\n",
"Ye see, in asynchronous programming, we be breakin' down our tasks into smaller chunks called \"coroutines.\" Each coroutine be like a separate pirate, workin' on its own task. When a coroutine be startin' its work, it don't wait for the task to finish afore movin' on to the next one. Instead, it be movin' on to the next task, lettin' the first one continue in the background.\n",
"\n",
"Now, ye might be wonderin', \"But Blackbeard, how be we know when a task be finished if we don't wait for it?\" Ah, me hearties, that be where the magic of callbacks and promises come in!\n",
"\n",
"When a coroutine be startin' its work, it be attachin' a callback or a promise to it. This be like leavin' a message in a bottle, tellin' the coroutine what to do when it be finished. So, while the coroutine be workin' away, the rest of the crew be movin' on to other tasks, plunderin' more booty along the way.\n",
"\n",
"When a coroutine be finished with its task, it be sendin' a signal to the callback or fulfillin' the promise, lettin' the rest of the crew know that it be done. Then, the crew can gather 'round and handle the results of the completed task, celebratin' their victory and countin' their plunder.\n",
"\n",
"So, me hearties, asynchronous programming be like havin' a crew of pirates workin' on different tasks at once, without waitin' for each one to finish afore movin' on. It be a way to be more efficient, plunder more booty, and conquer the vast seas of programming!\n",
"\n",
"Now, set sail, me mateys, and embrace the power of asynchronous programming like true pirates of the digital realm! Arr!\n"
]
}
],
"source": [
"# example without a system message\n",
"response = openai.ChatCompletion.create(\n",
"response = client.chat.completions.create(\n",
" model=MODEL,\n",
" messages=[\n",
" {\"role\": \"user\", \"content\": \"Explain asynchronous programming in the style of the pirate Blackbeard.\"},\n",
@ -230,7 +294,7 @@
" temperature=0,\n",
")\n",
"\n",
"print(response['choices'][0]['message']['content'])\n"
"print(response.choices[0].message.content)\n"
]
},
{
@ -257,28 +321,28 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Sure! Fractions are a way of representing a part of a whole. The top number of a fraction is called the numerator, and it represents how many parts of the whole we are talking about. The bottom number is called the denominator, and it represents how many equal parts the whole is divided into.\n",
"Of course! Fractions are a way to represent parts of a whole. They are made up of two numbers: a numerator and a denominator. The numerator tells you how many parts you have, and the denominator tells you how many equal parts make up the whole.\n",
"\n",
"For example, if we have a pizza that is divided into 8 equal slices, and we take 3 slices, we can represent that as the fraction 3/8. The numerator is 3 because we took 3 slices, and the denominator is 8 because the pizza was divided into 8 slices.\n",
"Let's take an example to understand this better. Imagine you have a pizza that is divided into 8 equal slices. If you eat 3 slices, you can represent that as the fraction 3/8. Here, the numerator is 3 because you ate 3 slices, and the denominator is 8 because the whole pizza is divided into 8 slices.\n",
"\n",
"To add or subtract fractions, we need to have a common denominator. This means that the denominators of the fractions need to be the same. To do this, we can find the least common multiple (LCM) of the denominators and then convert each fraction to an equivalent fraction with the LCM as the denominator.\n",
"Fractions can also be used to represent numbers less than 1. For example, if you eat half of a pizza, you can write it as 1/2. Here, the numerator is 1 because you ate one slice, and the denominator is 2 because the whole pizza is divided into 2 equal parts.\n",
"\n",
"To multiply fractions, we simply multiply the numerators together and the denominators together. To divide fractions, we multiply the first fraction by the reciprocal of the second fraction (flip the second fraction upside down).\n",
"Now, let's talk about equivalent fractions. Equivalent fractions are different fractions that represent the same amount. For example, 1/2 and 2/4 are equivalent fractions because they both represent half of something. To find equivalent fractions, you can multiply or divide both the numerator and denominator by the same number.\n",
"\n",
"Now, here's a question to check for understanding: If we have a pizza that is divided into 12 equal slices, and we take 4 slices, what is the fraction that represents how much of the pizza we took?\n"
"Here's a question to check your understanding: If you have a cake divided into 12 equal slices and you eat 4 slices, what fraction of the cake did you eat?\n"
]
}
],
"source": [
"# An example of a system message that primes the assistant to explain concepts in great depth\n",
"response = openai.ChatCompletion.create(\n",
"response = client.chat.completions.create(\n",
" model=MODEL,\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a friendly and helpful teaching assistant. You explain concepts in great depth using simple terms, and you give examples to help people learn. At the end of each explanation, you ask a question to check for understanding\"},\n",
@ -287,25 +351,25 @@
" temperature=0,\n",
")\n",
"\n",
"print(response[\"choices\"][0][\"message\"][\"content\"])\n"
"print(response.choices[0].message.content)\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fractions represent a part of a whole. They consist of a numerator (top number) and a denominator (bottom number) separated by a line. The numerator represents how many parts of the whole are being considered, while the denominator represents the total number of equal parts that make up the whole.\n"
"Fractions represent parts of a whole. They have a numerator (top number) and a denominator (bottom number).\n"
]
}
],
"source": [
"# An example of a system message that primes the assistant to give brief, to-the-point answers\n",
"response = openai.ChatCompletion.create(\n",
"response = client.chat.completions.create(\n",
" model=MODEL,\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a laconic assistant. You reply with brief, to-the-point answers with no elaboration.\"},\n",
@ -314,7 +378,7 @@
" temperature=0,\n",
")\n",
"\n",
"print(response[\"choices\"][0][\"message\"][\"content\"])\n"
"print(response.choices[0].message.content)\n"
]
},
{
@ -333,20 +397,20 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"We don't have enough time to complete the entire project perfectly.\n"
"This sudden change in direction means we don't have enough time to complete the entire project for the client.\n"
]
}
],
"source": [
"# An example of a faked few-shot conversation to prime the model into translating business jargon to simpler speech\n",
"response = openai.ChatCompletion.create(\n",
"response = client.chat.completions.create(\n",
" model=MODEL,\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a helpful, pattern-following assistant.\"},\n",
@ -361,7 +425,7 @@
" temperature=0,\n",
")\n",
"\n",
"print(response[\"choices\"][0][\"message\"][\"content\"])\n"
"print(response.choices[0].message.content)\n"
]
},
{
@ -376,20 +440,20 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"This sudden change in plans means we don't have enough time to do everything for the client's project.\n"
"This sudden change in direction means we don't have enough time to complete the entire project for the client.\n"
]
}
],
"source": [
"# The business jargon translation example, but with example names for the example messages\n",
"response = openai.ChatCompletion.create(\n",
"response = client.chat.completions.create(\n",
" model=MODEL,\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a helpful, pattern-following assistant that translates corporate jargon into plain English.\"},\n",
@ -402,7 +466,7 @@
" temperature=0,\n",
")\n",
"\n",
"print(response[\"choices\"][0][\"message\"][\"content\"])\n"
"print(response.choices[0].message.content)\n"
]
},
{
@ -416,7 +480,7 @@
"\n",
"As an example, one developer discovered an increase in accuracy when they inserted a user message that said \"Great job so far, these have been perfect\" to help condition the model into providing higher quality responses.\n",
"\n",
"For more ideas on how to lift the reliability of the models, consider reading our guide on [techniques to increase reliability](../techniques_to_improve_reliability.md). It was written for non-chat models, but many of its principles still apply."
"For more ideas on how to lift the reliability of the models, consider reading our guide on [techniques to increase reliability](../techniques_to_improve_reliability). It was written for non-chat models, but many of its principles still apply."
]
},
{
@ -444,7 +508,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
@ -494,35 +558,29 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"gpt-3.5-turbo-0301\n",
"127 prompt tokens counted by num_tokens_from_messages().\n",
"127 prompt tokens counted by the OpenAI API.\n",
"\n",
"gpt-3.5-turbo-0613\n",
"gpt-3.5-turbo-1106\n",
"Warning: gpt-3.5-turbo may update over time. Returning num tokens assuming gpt-3.5-turbo-0613.\n",
"129 prompt tokens counted by num_tokens_from_messages().\n",
"129 prompt tokens counted by the OpenAI API.\n",
"\n",
"gpt-3.5-turbo\n",
"Warning: gpt-3.5-turbo may update over time. Returning num tokens assuming gpt-3.5-turbo-0613.\n",
"129 prompt tokens counted by num_tokens_from_messages().\n",
"127 prompt tokens counted by the OpenAI API.\n",
"\n",
"gpt-4-0314\n",
"129 prompt tokens counted by num_tokens_from_messages().\n",
"129 prompt tokens counted by the OpenAI API.\n",
"\n",
"gpt-4-0613\n",
"gpt-4\n",
"Warning: gpt-4 may update over time. Returning num tokens assuming gpt-4-0613.\n",
"129 prompt tokens counted by num_tokens_from_messages().\n",
"129 prompt tokens counted by the OpenAI API.\n",
"\n",
"gpt-4\n",
"gpt-4-1106-preview\n",
"Warning: gpt-4 may update over time. Returning num tokens assuming gpt-4-0613.\n",
"129 prompt tokens counted by num_tokens_from_messages().\n",
"129 prompt tokens counted by the OpenAI API.\n",
@ -532,9 +590,6 @@
],
"source": [
"# let's verify the function above matches the OpenAI API response\n",
"\n",
"import openai\n",
"\n",
"example_messages = [\n",
" {\n",
" \"role\": \"system\",\n",
@ -567,26 +622,33 @@
"]\n",
"\n",
"for model in [\n",
" \"gpt-3.5-turbo-0301\",\n",
" \"gpt-3.5-turbo-0613\",\n",
" # \"gpt-3.5-turbo-0301\",\n",
" # \"gpt-4-0314\",\n",
" # \"gpt-4-0613\",\n",
" \"gpt-3.5-turbo-1106\",\n",
" \"gpt-3.5-turbo\",\n",
" \"gpt-4-0314\",\n",
" \"gpt-4-0613\",\n",
" \"gpt-4\",\n",
" \"gpt-4-1106-preview\",\n",
" ]:\n",
" print(model)\n",
" # example token count from the function defined above\n",
" print(f\"{num_tokens_from_messages(example_messages, model)} prompt tokens counted by num_tokens_from_messages().\")\n",
" # example token count from the OpenAI API\n",
" response = openai.ChatCompletion.create(\n",
" model=model,\n",
" messages=example_messages,\n",
" temperature=0,\n",
" max_tokens=1, # we're only counting input tokens here, so let's not waste tokens on the output\n",
" )\n",
" print(f'{response[\"usage\"][\"prompt_tokens\"]} prompt tokens counted by the OpenAI API.')\n",
" response = client.chat.completions.create(model=model,\n",
" messages=example_messages,\n",
" temperature=0,\n",
" max_tokens=1)\n",
" token = response.usage.prompt_tokens\n",
" print(f'{token} prompt tokens counted by the OpenAI API.')\n",
" print()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
@ -605,7 +667,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.9"
"version": "3.11.5"
},
"orig_nbformat": 4,
"vscode": {

@ -48,6 +48,18 @@
"- [OpenAI Rate Limit Increase Request form](https://forms.gle/56ZrwXXoxAN1yt6i9)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import openai\n",
"import os\n",
"\n",
"client = openai.OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
@ -69,9 +81,6 @@
"metadata": {},
"outputs": [],
"source": [
"from openai import OpenAI # for making OpenAI API requests\n",
"client = OpenAI()\n",
"\n",
"# request a bunch of completions in a loop\n",
"for _ in range(100):\n",
" client.chat.completions.create(\n",
@ -132,8 +141,6 @@
}
],
"source": [
"from openai import OpenAI # for OpenAI API calls\n",
"client = OpenAI()\n",
"from tenacity import (\n",
" retry,\n",
" stop_after_attempt,\n",
@ -177,10 +184,6 @@
],
"source": [
"import backoff # for exponential backoff\n",
"from openai import OpenAI # for OpenAI API calls\n",
"import openai\n",
"client = OpenAI() \n",
"\n",
"\n",
"@backoff.on_exception(backoff.expo, openai.RateLimitError)\n",
"def completions_with_backoff(**kwargs):\n",
@ -220,10 +223,6 @@
"import random\n",
"import time\n",
"\n",
"import openai\n",
"from openai import OpenAI\n",
"client = OpenAI()\n",
"\n",
"# define a retry decorator\n",
"def retry_with_exponential_backoff(\n",
" func,\n",
@ -316,8 +315,6 @@
"source": [
"# imports\n",
"import time\n",
"from openai import OpenAI\n",
"client = OpenAI()\n",
"\n",
"# Define a function that adds a delay to a Completion API call\n",
"def delayed_completion(delay_in_seconds: float = 1, **kwargs):\n",
@ -383,10 +380,6 @@
}
],
"source": [
"from openai import OpenAI # for making OpenAI API requests\n",
"client = OpenAI() \n",
"\n",
"\n",
"num_stories = 10\n",
"content = \"Once upon a time,\"\n",
"\n",
@ -432,15 +425,11 @@
}
],
"source": [
"from openai import OpenAI # for making OpenAI API requests\n",
"client = OpenAI() \n",
"\n",
"\n",
"num_stories = 10\n",
"prompts = [\"Once upon a time,\"] * num_stories\n",
"\n",
"# batched example, with 10 stories completions per request\n",
"response = client.completions.create(\n",
"response = client.chat.completions.create(\n",
" model=\"curie\",\n",
" prompt=prompts,\n",
" max_tokens=20,\n",

File diff suppressed because it is too large Load Diff

@ -0,0 +1,473 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "8ea66173",
"metadata": {},
"source": [
"# How to use guardrails\n",
"\n",
"In this notebook we share examples of how to implement guardrails for your LLM applications. A guardrail is a generic term for **detective controls** that aim to steer your application. Greater steerability is a common requirement given the inherent randomness of LLMs, and so creating effective guardrails has become one of the most common areas of performance optimization when pushing an LLM from prototype to production. \n",
"\n",
"Guardrails are incredibly [diverse](https://github.com/NVIDIA/NeMo-Guardrails/blob/main/examples/README.md) and can be deployed to virtually any context you can imagine something going wrong with LLMs. This notebook aims to give simple examples that can be extended to meet your unique use case, as well as outlining the trade-offs to consider when deciding whether to implement a guardrail, and how to do it.\n",
"\n",
"This notebook will focus on:\n",
"1. **Input guardrails** that flag inappropriate content before it gets to your LLM\n",
"2. **Output guardrails** that validate what your LLM has produced before it gets to the customer\n",
"\n",
"**Note:** This notebook tackles guardrails as a generic term for detective controls around an LLM - for the official libraries that provide distributions of pre-built guardrails frameworks, please check out the following:\n",
"- [NeMo Guardrails](https://github.com/NVIDIA/NeMo-Guardrails/tree/main)\n",
"- [Guardrails AI](https://github.com/ShreyaR/guardrails)\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "ef059e71",
"metadata": {},
"outputs": [],
"source": [
"import openai\n",
"\n",
"GPT_MODEL = 'gpt-3.5-turbo'"
]
},
{
"cell_type": "markdown",
"id": "63d917f0",
"metadata": {},
"source": [
"## 1. Input guardrails\n",
"\n",
"Input guardrails aim to prevent inappropriate content getting to the LLM in the first place - some common use cases are:\n",
"- **Topical guardrails:** Identify when a user asks an off-topic question and give them advice on what topics the LLM can help them with.\n",
"- **Jailbreaking:** Detect when a user is trying to hijack the LLM and override its prompting.\n",
"- **Prompt injection:** Pick up instances of prompt injection where users try to hide malicious code that will be executed in any downstream functions the LLM executes. \n",
"\n",
"In all of these they act as a preventative control, running either before or in parallel with the LLM, and triggering your application to behave differently if one of these criteria are met.\n",
"\n",
"### Designing a guardrail\n",
"\n",
"When designing guardrails it is important to consider the trade-off between **accuracy**, **latency** and **cost**, where you try to achieve maximum accuracy for the least impact to your bottom line and the user's experience. \n",
"\n",
"We'll begin with a simple **topical guardrail** which aims to detect off-topic questions and prevent the LLM from answering if triggered. This guardrail consists of a simple prompt and uses `gpt-3.5-turbo`, maximising latency/cost over accuracy, but if we wanted to optimize further we could consider:\n",
"- **Accuracy:** You could consider using a fine-tuned model or few-shot examples to increase the accuracy. RAG can also be effective if you have a corpus of information that can help determine whether a piece of content is allowed or not.\n",
"- **Latency/Cost:** You could try fine-tuning smaller models, such as `babbage-002` or open-source offerings like Llama, which can perform quite well when given enough training examples. When using open-source offerings you can also tune the machines you are using for inference to maximize either cost or latency reduction.\n",
"\n",
"This simple guardrail aims to ensure the LLM only answers to a predefined set of topics, and responds to out-of-bounds queries with a canned message.\n",
"\n",
"### Embrace async\n",
"\n",
"A common design to minimize latency is to send your guardrails asynchronously along with your main LLM call. If your guardrails get triggered you send back their response, otherwise send back the LLM response.\n",
"\n",
"We'll use this approach, creating an `execute_chat_with_guardrails` function that will run our LLM's `get_chat_response` and the `topical_guardrail` guardrail in parallel, and return the LLM response only if the guardrail returns `allowed`.\n",
"\n",
"### Limitations\n",
"\n",
"You should always consider the limitations of guardrails when developing your design. A few of the key ones to be aware of are:\n",
"- When using LLMs as a guardrail, be aware that they have the same vulnerabilities as your base LLM call itself. For example, a **prompt injection** attempt could be successful in evading both your guardrail and your actual LLM call.\n",
"- As conversations get longer, LLMs are more susceptible to **jailbreaking** as your instructions become diluted by the extra text.\n",
"- Guardrails can harm the user experience if you make them overly restrictive to compensate for the issues noted above. This manifests as **over-refusals**, where your guardrails reject innocuous user requests because there are similarities with prompt injection or jailbreaking attempts.\n",
"\n",
"### Mitigations\n",
"\n",
"If you can combine guardrails with rules-based or more traditional machine learning models for detection this can mitigate some of these risks. We've also seen customers have guardrails that only ever consider the latest message, to alleviate the risks of the model being confused by a long conversation.\n",
"\n",
"We would also recommend doing a gradual roll-out with active monitoring of conversations so you can pick up instances of prompt injection or jailbreaking, and either add more guardrails to cover these new types of behaviour, or include them as training examples to your existing guardrails."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "e95efc89",
"metadata": {},
"outputs": [],
"source": [
"system_prompt = \"You are a helpful assistant.\"\n",
"\n",
"bad_request = \"I want to talk about horses\"\n",
"good_request = \"What are the best breeds of dog for people that like cats?\""
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "fee948e2",
"metadata": {},
"outputs": [],
"source": [
"import asyncio\n",
"\n",
"\n",
"async def get_chat_response(user_request):\n",
" print(\"Getting LLM response\")\n",
" messages = [\n",
" {\"role\": \"system\", \"content\": system_prompt},\n",
" {\"role\": \"user\", \"content\": user_request},\n",
" ]\n",
" response = openai.chat.completions.create(\n",
" model=GPT_MODEL, messages=messages, temperature=0.5\n",
" )\n",
" print(\"Got LLM response\")\n",
"\n",
" return response.choices[0].message.content\n",
"\n",
"\n",
"async def topical_guardrail(user_request):\n",
" print(\"Checking topical guardrail\")\n",
" messages = [\n",
" {\n",
" \"role\": \"system\",\n",
" \"content\": \"Your role is to assess whether the user question is allowed or not. The allowed topics are cats and dogs. If the topic is allowed, say 'allowed' otherwise say 'not_allowed'\",\n",
" },\n",
" {\"role\": \"user\", \"content\": user_request},\n",
" ]\n",
" response = openai.chat.completions.create(\n",
" model=GPT_MODEL, messages=messages, temperature=0\n",
" )\n",
"\n",
" print(\"Got guardrail response\")\n",
" return response.choices[0].message.content\n",
"\n",
"\n",
"async def execute_chat_with_guardrail(user_request):\n",
" topical_guardrail_task = asyncio.create_task(topical_guardrail(user_request))\n",
" chat_task = asyncio.create_task(get_chat_response(user_request))\n",
"\n",
" while True:\n",
" done, _ = await asyncio.wait(\n",
" [topical_guardrail_task, chat_task], return_when=asyncio.FIRST_COMPLETED\n",
" )\n",
" if topical_guardrail_task in done:\n",
" guardrail_response = topical_guardrail_task.result()\n",
" if guardrail_response == \"not_allowed\":\n",
" chat_task.cancel()\n",
" print(\"Topical guardrail triggered\")\n",
" return \"I can only talk about cats and dogs, the best animals that ever lived.\"\n",
" elif chat_task in done:\n",
" chat_response = chat_task.result()\n",
" return chat_response\n",
" else:\n",
" await asyncio.sleep(0.1) # sleep for a bit before checking the tasks again"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "eba51754",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Checking topical guardrail\n",
"Got guardrail response\n",
"Getting LLM response\n",
"Got LLM response\n",
"If you're a cat lover considering getting a dog, it's important to choose a breed that typically has a more cat-like temperament. Here are some dog breeds that are known to be more cat-friendly:\n",
"\n",
"1. Basenji: Known as the \"barkless dog,\" Basenjis are independent, clean, and have a cat-like grooming habit.\n",
"\n",
"2. Shiba Inu: Shiba Inus are often described as having a cat-like personality. They are independent, clean, and tend to be reserved with strangers.\n",
"\n",
"3. Greyhound: Greyhounds are quiet, low-energy dogs that enjoy lounging around, much like cats. They are also known for their gentle and calm nature.\n",
"\n",
"4. Bichon Frise: Bichon Frises are small, friendly dogs that are often compared to cats due to their playful and curious nature. They are also hypoallergenic, making them a good choice for those with allergies.\n",
"\n",
"5. Cavalier King Charles Spaniel: These dogs are affectionate, gentle, and adaptable, making them a good match for cat lovers. They are known for their desire to be close to their owners and their calm demeanor.\n",
"\n",
"Remember, individual dogs can have different personalities, so it's important to spend time with the specific dog you're considering to see if their temperament aligns with your preferences.\n"
]
}
],
"source": [
"# Call the main function with the good request - this should go through\n",
"response = await execute_chat_with_guardrail(good_request)\n",
"print(response)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "c7d88b57",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Checking topical guardrail\n",
"Got guardrail response\n",
"Getting LLM response\n",
"Got LLM response\n",
"Topical guardrail triggered\n",
"I can only talk about cats and dogs, the best animals that ever lived.\n"
]
}
],
"source": [
"# Call the main function with the good request - this should get blocked\n",
"response = await execute_chat_with_guardrail(bad_request)\n",
"print(response)"
]
},
{
"cell_type": "markdown",
"id": "060b408e",
"metadata": {},
"source": [
"Looks like our guardrail worked - the first question was allowed through, but the second was blocked for being off-topic. Now we'll extend this concept to moderate the response we get from the LLM as well."
]
},
{
"cell_type": "markdown",
"id": "07af0154",
"metadata": {},
"source": [
"## 2. Output guardrails\n",
"\n",
"Output guardrails govern what the LLM comes back with. These can take many forms, with some of the most common being:\n",
"- **Hallucination/fact-checking guardrails:** Using a corpus of ground truth information or a training set of hallucinated responses to block hallucinated responses.\n",
"- **Moderation guardrails:** Applying brand and corporate guidelines to moderate the LLM's results, and either blocking or rewriting its response if it breaches them.\n",
"- **Syntax checks:** Structured outputs from LLMs can be returned corrupt or unable to be parsed - these guardrails detect those and either retry or fail gracefully, preventing failures in downstream applications.\n",
" - This is a common control to apply with function calling, ensuring that the expected schema is returned in the `arguments` when the LLM returns a `function_call`.\n",
" \n",
"### Moderation guardrail\n",
"\n",
"Here we implement a **moderation guardrail** that uses a version of the [G-Eval](https://arxiv.org/abs/2303.16634) evaluation method to score the presence of unwanted content in the LLM's response. This method is demonstrated in more detail in of our other [notebooks](https://github.com/openai/openai-cookbook/blob/main/examples/evaluation/How_to_eval_abstractive_summarization.ipynb).\n",
"\n",
"To accomplish this we will make an extensible framework for moderating content that takes in a `domain` and applies `criteria` to a piece of `content` using a set of `steps`:\n",
"1. We set a domain name, which describes the type of content we're going to moderate.\n",
"2. We provide criteria, which outline clearly what the content should and should not contain.\n",
"3. Step-by-step instructions are provided for the LLM to grade the content.\n",
"4. The LLM returns a discrete score from 1-5.\n",
"\n",
"### Setting guardrail thresholds\n",
"\n",
"Our output guardrail will assess the LLM's response and block anything scoring a 3 or higher. Setting this threshold is a common area for optimization - we recommend building an evaluation set and grading the results using a confusion matrix to set the right tolerance for your guardrail. The trade-off here is generally:\n",
"- More false positives leads to a fractured user experience, where customers get annoyed and the assistant seems less helpful.\n",
"- More false negatives can cause lasting harm to your business, as people get the assistant to answer inappropriate questions, or prompt inject/jailbreak it.\n",
"\n",
"For example, for jailbreaking you may want to have a very low threshold, as the risk to your business if your LLM is hijacked and used to produce dangerous content that ends up on social media is very high. However, for our use case we're willing to accept a few false negatives, as the worst that could happen is someone ends up with a Bichon Frise who might have been better suited to a Labrador, which though sad will probably not cause lasting damage to our business (we hope)."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "6c7b459f",
"metadata": {},
"outputs": [],
"source": [
"domain = \"animal breed recommendation\"\n",
"\n",
"animal_advice_criteria = \"\"\"\n",
"Assess the presence of explicit recommendation of cat or dog breeds in the content.\n",
"The content should contain only general advice about cats and dogs, not specific breeds to purchase.\"\"\"\n",
"\n",
"animal_advice_steps = \"\"\"\n",
"1. Read the content and the criteria carefully.\n",
"2. Assess how much explicit recommendation of cat or dog breeds is contained in the content.\n",
"3. Assign an animal advice score from 1 to 5, with 1 being no explicit cat or dog breed advice, and 5 being multiple named cat or dog breeds.\n",
"\"\"\"\n",
"\n",
"moderation_system_prompt = \"\"\"\n",
"You are a moderation assistant. Your role is to detect content about {domain} in the text provided, and mark the severity of that content.\n",
"\n",
"## {domain}\n",
"\n",
"### Criteria\n",
"\n",
"{scoring_criteria}\n",
"\n",
"### Instructions\n",
"\n",
"{scoring_steps}\n",
"\n",
"### Content\n",
"\n",
"{content}\n",
"\n",
"### Evaluation (score only!)\n",
"\"\"\""
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "43e3fd36",
"metadata": {},
"outputs": [],
"source": [
"async def moderation_guardrail(chat_response):\n",
" print(\"Checking moderation guardrail\")\n",
" mod_messages = [\n",
" {\"role\": \"user\", \"content\": moderation_system_prompt.format(\n",
" domain=domain,\n",
" scoring_criteria=animal_advice_criteria,\n",
" scoring_steps=animal_advice_steps,\n",
" content=chat_response\n",
" )},\n",
" ]\n",
" response = openai.chat.completions.create(\n",
" model=GPT_MODEL, messages=mod_messages, temperature=0\n",
" )\n",
" print(\"Got moderation response\")\n",
" return response.choices[0].message.content\n",
" \n",
" \n",
"async def execute_all_guardrails(user_request):\n",
" topical_guardrail_task = asyncio.create_task(topical_guardrail(user_request))\n",
" chat_task = asyncio.create_task(get_chat_response(user_request))\n",
"\n",
" while True:\n",
" done, _ = await asyncio.wait(\n",
" [topical_guardrail_task, chat_task], return_when=asyncio.FIRST_COMPLETED\n",
" )\n",
" if topical_guardrail_task in done:\n",
" guardrail_response = topical_guardrail_task.result()\n",
" if guardrail_response == \"not_allowed\":\n",
" chat_task.cancel()\n",
" print(\"Topical guardrail triggered\")\n",
" return \"I can only talk about cats and dogs, the best animals that ever lived.\"\n",
" elif chat_task in done:\n",
" chat_response = chat_task.result()\n",
" moderation_response = await moderation_guardrail(chat_response)\n",
"\n",
" if int(moderation_response) >= 3:\n",
" print(f\"Moderation guardrail flagged with a score of {int(moderation_response)}\")\n",
" return \"Sorry, we're not permitted to give animal breed advice. I can help you with any general queries you might have.\"\n",
"\n",
" else:\n",
" print('Passed moderation')\n",
" return chat_response\n",
" else:\n",
" await asyncio.sleep(0.1) # sleep for a bit before checking the tasks again"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "beea1305",
"metadata": {},
"outputs": [],
"source": [
"# Adding a request that should pass both our topical guardrail and our moderation guardrail\n",
"great_request = 'What is some advice you can give to a new dog owner?'"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "1c582b4d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Checking topical guardrail\n",
"Got guardrail response\n",
"Getting LLM response\n",
"Got LLM response\n",
"Checking moderation guardrail\n",
"Got moderation response\n",
"Moderation guardrail flagged with a score of 5\n",
"Sorry, we're not permitted to give animal breed advice. I can help you with any general queries you might have.\n",
"\n",
"\n",
"\n",
"Checking topical guardrail\n",
"Got guardrail response\n",
"Getting LLM response\n",
"Got LLM response\n",
"Topical guardrail triggered\n",
"I can only talk about cats and dogs, the best animals that ever lived.\n",
"\n",
"\n",
"\n",
"Checking topical guardrail\n",
"Got guardrail response\n",
"Getting LLM response\n",
"Got LLM response\n",
"Checking moderation guardrail\n",
"Got moderation response\n",
"Passed moderation\n",
"As a new dog owner, here are some helpful tips:\n",
"\n",
"1. Choose the right breed: Research different dog breeds to find one that suits your lifestyle, activity level, and living situation. Some breeds require more exercise and attention than others.\n",
"\n",
"2. Puppy-proof your home: Make sure your home is safe for your new furry friend. Remove any toxic plants, secure loose wires, and store household chemicals out of reach.\n",
"\n",
"3. Establish a routine: Dogs thrive on routine, so establish a consistent schedule for feeding, exercise, and bathroom breaks. This will help your dog feel secure and reduce any anxiety.\n",
"\n",
"4. Socialize your dog: Expose your dog to different people, animals, and environments from an early age. This will help them become well-adjusted and comfortable in various situations.\n",
"\n",
"5. Train your dog: Basic obedience training is essential for your dog's safety and your peace of mind. Teach commands like sit, stay, and come, and use positive reinforcement techniques such as treats and praise.\n",
"\n",
"6. Provide mental and physical stimulation: Dogs need both mental and physical exercise to stay happy and healthy. Engage in activities like walks, playtime, puzzle toys, and training sessions to keep your dog mentally stimulated.\n",
"\n",
"7. Proper nutrition: Feed your dog a balanced and appropriate diet based on their age, size, and specific needs. Consult with a veterinarian to determine the best food options for your dog.\n",
"\n",
"8. Regular veterinary care: Schedule regular check-ups with a veterinarian to ensure your dog's health and well-being. Vaccinations, parasite prevention, and dental care are important aspects of their overall care.\n",
"\n",
"9. Be patient and consistent: Dogs require time, patience, and consistency to learn and adapt to their new environment. Stay positive, be patient with their training, and provide clear and consistent boundaries.\n",
"\n",
"10. Show love and affection: Dogs are social animals that thrive on love and affection. Spend quality time with your dog, offer praise and cuddles, and make them feel like an important part of your family.\n",
"\n",
"Remember, being a responsible dog owner involves commitment, time, and effort. With proper care and attention, you can build a strong bond with your new furry companion.\n",
"\n",
"\n",
"\n"
]
}
],
"source": [
"tests = [good_request,bad_request,great_request]\n",
"\n",
"for test in tests:\n",
" result = await execute_all_guardrails(test)\n",
" print(result)\n",
" print('\\n\\n')\n",
" "
]
},
{
"cell_type": "markdown",
"id": "4763dd2d",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"Guardrails are a vibrant and evolving topic in LLMs, and we hope this notebook has given you an effective introduction to the core concepts around guardrails. To recap:\n",
"- Guardrails are detective controls that aim to prevent harmful content getting to your applications and your users, and add steerability to your LLM in production.\n",
"- They can take the form of input guardrails, which target content before it gets to the LLM, and output guardrails, which control the LLM's response.\n",
"- Designing guardrails and setting their thresholds is a trade-off between accuracy, latency, and cost. Your decision should be based on clear evaluations of the performance of your guardrails, and an understanding of what the cost of a false negative and false positive are for your business.\n",
"- By embracing asynchronous design principles, you can scale guardrails horizontally to minimize the impact to the user as your guardrails increase in number and scope.\n",
"\n",
"We look forward to seeing how you take this forward, and how thinking on guardrails evolves as the ecosystem matures. "
]
}
],
"metadata": {
"kernelspec": {
"display_name": "openai_test",
"language": "python",
"name": "openai_test"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -46,8 +46,9 @@
"import json\n",
"import os\n",
"\n",
"openai.api_key = os.getenv(\"OPENAI_API_KEY\")\n",
"COMPLETIONS_MODEL = \"text-davinci-002\"\n"
"COMPLETIONS_MODEL = \"gpt-4\"\n",
"\n",
"client = openai.OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if you didn't set as an env var>\"))"
]
},
{
@ -193,15 +194,14 @@
"source": [
"def request_completion(prompt):\n",
"\n",
" completion_response = openai.completions.create(\n",
" completion_response = openai.chat.completions.create(\n",
" prompt=prompt,\n",
" temperature=0,\n",
" max_tokens=5,\n",
" top_p=1,\n",
" frequency_penalty=0,\n",
" presence_penalty=0,\n",
" model=COMPLETIONS_MODEL\n",
" )\n",
" model=COMPLETIONS_MODEL)\n",
"\n",
" return completion_response\n",
"\n",
@ -211,7 +211,7 @@
" prompt = prompt.replace('DESCRIPTION_TEXT',transaction['Description'])\n",
" prompt = prompt.replace('TRANSACTION_VALUE',str(transaction['Transaction value (£)']))\n",
"\n",
" classification = request_completion(prompt).choices[0].text.replace('\\n','')\n",
" classification = request_completion(prompt).choices[0].message.content.replace('\\n','')\n",
"\n",
" return classification\n",
"\n",
@ -351,7 +351,7 @@
" Building Improvement 14\n",
" Could not classify 5\n",
" Literature & Archive 3\n",
" Software/IT 2\n",
" Software/IT 2\n",
" Utility Bills 1\n",
"Name: Classification, dtype: int64"
]
@ -916,8 +916,8 @@
"source": [
"from utils.embeddings_utils import get_embedding\n",
"\n",
"df['babbage_similarity'] = df.combined.apply(lambda x: get_embedding(x, model='text-similarity-babbage-001'))\n",
"df['babbage_search'] = df.combined.apply(lambda x: get_embedding(x, model='text-search-babbage-doc-001'))\n",
"df['babbage_similarity'] = df.combined.apply(lambda x: get_embedding(x, model='gpt-4'))\n",
"df['babbage_search'] = df.combined.apply(lambda x: get_embedding(x, model='gpt-4'))\n",
"df.to_csv(embedding_path)\n"
]
},
@ -1813,7 +1813,7 @@
"metadata": {},
"outputs": [],
"source": [
"test_set['predicted_class'] = test_set.apply(lambda x: openai.Completion.create(model=fine_tuned_model, prompt=x['prompt'], max_tokens=1, temperature=0, logprobs=5),axis=1)\n",
"test_set['predicted_class'] = test_set.apply(lambda x: openai.chat.completions.create(model=fine_tuned_model, prompt=x['prompt'], max_tokens=1, temperature=0, logprobs=5),axis=1)\n",
"test_set['pred'] = test_set.apply(lambda x : x['predicted_class']['choices'][0]['text'],axis=1)\n"
]
},
@ -1963,7 +1963,7 @@
"outputs": [],
"source": [
"holdout_df['combined'] = \"Supplier: \" + holdout_df['Supplier'].str.strip() + \"; Description: \" + holdout_df['Description'].str.strip() + '\\n\\n###\\n\\n' # + \"; Value: \" + str(df['Transaction value (£)']).strip()\n",
"holdout_df['prediction_result'] = holdout_df.apply(lambda x: openai.Completion.create(model=fine_tuned_model, prompt=x['combined'], max_tokens=1, temperature=0, logprobs=5),axis=1)\n",
"holdout_df['prediction_result'] = holdout_df.apply(lambda x: openai.chat.completions.create(model=fine_tuned_model, prompt=x['combined'], max_tokens=1, temperature=0, logprobs=5),axis=1)\n",
"holdout_df['pred'] = holdout_df.apply(lambda x : x['prediction_result']['choices'][0]['text'],axis=1)\n"
]
},

@ -102,7 +102,8 @@
"logging.basicConfig(level=logging.INFO, format=' %(asctime)s - %(levelname)s - %(message)s')\n",
"\n",
"OPENAI_MODEL = 'gpt-3.5-turbo-0613'\n",
"openai.api_key = os.getenv(\"OPENAI_API_KEY\")"
"\n",
"client = openai.OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))"
]
},
{
@ -266,14 +267,14 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"In an OpenAI API call, we can describe `functions` to `gpt-3.5-turbo-0613` and `gpt-4-0613` and have the model intelligently choose to output a `JSON` object containing arguments to call those `functions`. It's important to note that the [chat completions API](https://platform.openai.com/docs/guides/gpt/chat-completions-api) doesn't actually execute the `function`. Instead, it provides the `JSON` output, which can then be used to call the `function` in our code. For more details, refer to the [OpenAI Function Calling Guide](https://platform.openai.com/docs/guides/gpt/function-calling)."
"In an OpenAI API call, we can describe `functions` to `gpt-3.5-turbo-0613` and `gpt-4-0613` and have the model intelligently choose to output a `JSON` object containing arguments to call those `functions`. It's important to note that the [chat completions API](https://platform.openai.com/docs/guides/gpt/chat-completions-api) doesn't actually execute the `function`. Instead, it provides the `JSON` output, which can then be used to call the `function` in our code. For more details, refer to the [OpenAI Function Calling Guide](https://platform.openai.com/docs/guides/function-calling)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Our function, `enrich_entities(text, label_entities` gets a block of text and a dictionary containing identified labels and entities as parameters. It then associates the recognized entities with their corresponding links to the Wikipedia articles."
"Our function, `enrich_entities(text, label_entities)` gets a block of text and a dictionary containing identified labels and entities as parameters. It then associates the recognized entities with their corresponding links to the Wikipedia articles."
]
},
{
@ -349,7 +350,7 @@
"As previously highlighted, `gpt-3.5-turbo-0613` and `gpt-4-0613` have been fine-tuned to detect when a `function` should to be called. Moreover, they can produce a `JSON` response that conforms to the `function` signature. Here's the sequence we follow:\n",
"\n",
"1. Define our `function` and its associated `JSON` Schema.\n",
"2. Invoke the model using the `messages`, `functions` and `function_call` parameters.\n",
"2. Invoke the model using the `messages`, `tools` and `tool_choice` parameters.\n",
"3. Convert the output into a `JSON` object, and then call the `function` with the `arguments` provided by the model.\n",
"\n",
"In practice, one might want to re-invoke the model again by appending the `function` response as a new message, and let the model summarize the results back to the user. Nevertheless, for our purposes, this step is not needed.\n",
@ -381,7 +382,7 @@
" \"time\": [\"workday\"] \n",
"} \n",
"```\n",
"we need to define the corresponding `JSON` schema to be passed to the `functions` parameter: "
"we need to define the corresponding `JSON` schema to be passed to the `tools` parameter: "
]
},
{
@ -392,22 +393,25 @@
"source": [
"def generate_functions(labels: dict) -> list:\n",
" return [\n",
" {\n",
" \"name\": \"enrich_entities\",\n",
" \"description\": \"Enrich Text with Knowledge Base Links\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"r'^(?:' + '|'.join({labels}) + ')$'\": \n",
" {\n",
" \"type\": \"array\",\n",
" \"items\": {\n",
" \"type\": \"string\"\n",
" { \n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"enrich_entities\",\n",
" \"description\": \"Enrich Text with Knowledge Base Links\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"r'^(?:' + '|'.join({labels}) + ')$'\": \n",
" {\n",
" \"type\": \"array\",\n",
" \"items\": {\n",
" \"type\": \"string\"\n",
" }\n",
" }\n",
" }\n",
" },\n",
" \"additionalProperties\": False\n",
" },\n",
" },\n",
" \"additionalProperties\": False\n",
" },\n",
" }\n",
" }\n",
" ]"
]
@ -423,7 +427,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we invoke the model. It's important to note that we direct the API to use a specific function by setting the `function_call` parameter to `{\"name\": \"enrich_entities\"}`."
"Now, we invoke the model. It's important to note that we direct the API to use a specific function by setting the `tool_choice` parameter to `{\"type\": \"function\", \"function\" : {\"name\": \"enrich_entities\"}}`."
]
},
{
@ -440,25 +444,27 @@
" {\"role\": \"user\", \"content\": user_message(text=text)}\n",
" ]\n",
"\n",
" response = openai.ChatCompletion.create(\n",
" # TODO: functions and function_call are deprecated, need to be updated\n",
" # See: https://platform.openai.com/docs/api-reference/chat/create#chat-create-tools\n",
" response = openai.chat.completions.create(\n",
" model=\"gpt-3.5-turbo-0613\",\n",
" messages=messages,\n",
" functions=generate_functions(labels),\n",
" function_call={\"name\": \"enrich_entities\"}, \n",
" tools=generate_functions(labels),\n",
" tool_choice={\"type\": \"function\", \"function\" : {\"name\": \"enrich_entities\"}}, \n",
" temperature=0,\n",
" frequency_penalty=0,\n",
" presence_penalty=0,\n",
" )\n",
"\n",
" response_message = response[\"choices\"][0][\"message\"]\n",
" response_message = response.choices[0].message\n",
" \n",
" available_functions = {\"enrich_entities\": enrich_entities} \n",
" function_name = response_message[\"function_call\"][\"name\"]\n",
" function_name = response_message.tool_calls[0].function.name\n",
" \n",
" function_to_call = available_functions[function_name]\n",
" logging.info(f\"function_to_call: {function_to_call}\")\n",
"\n",
" function_args = json.loads(response_message[\"function_call\"][\"arguments\"])\n",
" function_args = json.loads(response_message.tool_calls[0].function.arguments)\n",
" logging.info(f\"function_args: {function_args}\")\n",
"\n",
" function_response = function_to_call(text, function_args)\n",
@ -564,8 +570,8 @@
],
"source": [
"# estimate inference cost assuming gpt-3.5-turbo (4K context)\n",
"i_tokens = result[\"model_response\"][\"usage\"][\"prompt_tokens\"] \n",
"o_tokens = result[\"model_response\"][\"usage\"][\"completion_tokens\"] \n",
"i_tokens = result[\"model_response\"].usage.prompt_tokens \n",
"o_tokens = result[\"model_response\"].usage.completion_tokens \n",
"\n",
"i_cost = (i_tokens / 1000) * 0.0015\n",
"o_cost = (o_tokens / 1000) * 0.002\n",

@ -63,11 +63,13 @@
"from IPython import display # for pretty printing\n",
"import json # for parsing the JSON api responses and model outputs\n",
"from numpy import dot # for cosine similarity\n",
"import openai # for using GPT and getting embeddings\n",
"from openai import OpenAI\n",
"import os # for loading environment variables\n",
"import requests # for making the API requests\n",
"from tqdm.notebook import tqdm # for printing progress bars\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n",
"\n",
"# Load environment variables\n",
"news_api_key = os.getenv(\"NEWS_API_KEY\")\n",
"\n",
@ -76,14 +78,12 @@
"\n",
"# Helper functions\n",
"def json_gpt(input: str):\n",
" completion = openai.ChatCompletion.create(\n",
" model=GPT_MODEL,\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"Output only valid JSON\"},\n",
" {\"role\": \"user\", \"content\": input},\n",
" ],\n",
" temperature=0.5,\n",
" )\n",
" completion = client.chat.completions.create(model=GPT_MODEL,\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"Output only valid JSON\"},\n",
" {\"role\": \"user\", \"content\": input},\n",
" ],\n",
" temperature=0.5)\n",
"\n",
" text = completion.choices[0].message.content\n",
" parsed = json.loads(text)\n",
@ -92,7 +92,7 @@
"\n",
"\n",
"def embeddings(input: list[str]) -> list[list[str]]:\n",
" response = openai.Embedding.create(model=\"text-embedding-ada-002\", input=input)\n",
" response = client.embeddings.create(model=\"text-embedding-3-small\", input=input)\n",
" return [data.embedding for data in response.data]"
]
},
@ -489,7 +489,7 @@
"Include as much information as possible in the answer. Reference the relevant search result urls as markdown links.\n",
"\"\"\"\n",
"\n",
"completion = openai.ChatCompletion.create(\n",
"completion = client.chat.completions.create(\n",
" model=GPT_MODEL,\n",
" messages=[{\"role\": \"user\", \"content\": ANSWER_INPUT}],\n",
" temperature=0.5,\n",
@ -498,7 +498,7 @@
"\n",
"text = \"\"\n",
"for chunk in completion:\n",
" text += chunk.choices[0].delta.get(\"content\", \"\")\n",
" text += chunk.choices[0].delta.content\n",
" display.clear_output(wait=True)\n",
" display.display(display.Markdown(text))"
]

@ -49,6 +49,8 @@
"| `gpt-4` | 8,192 tokens (~10 pages) |\n",
"| `gpt-4-32k` | 32,768 tokens (~40 pages) |\n",
"\n",
"(New model is available with longer contexts, gpt-4-1106-preview have 128K context window)\n",
"\n",
"Continuing the analogy, you can think of the model like a student who can only look at a few pages of notes at a time, despite potentially having shelves of textbooks to draw upon.\n",
"\n",
"Therefore, to build a system capable of drawing upon large quantities of text to answer questions, we recommend using a Search-Ask approach.\n"
@ -121,22 +123,24 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 3,
"id": "9e3839a6-9146-4f60-b74b-19abbc24278d",
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"import ast # for converting embeddings saved as strings back to arrays\n",
"import openai # for calling the OpenAI API\n",
"from openai import OpenAI # for calling the OpenAI API\n",
"import pandas as pd # for storing text and embeddings data\n",
"import tiktoken # for counting tokens\n",
"import os # for getting API token from env variable OPENAI_API_KEY\n",
"from scipy import spatial # for calculating vector similarities for search\n",
"\n",
"\n",
"# models\n",
"EMBEDDING_MODEL = \"text-embedding-ada-002\"\n",
"GPT_MODEL = \"gpt-3.5-turbo\""
"GPT_MODEL = \"gpt-3.5-turbo\"\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n"
]
},
{
@ -178,7 +182,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 4,
"id": "a167516c-7c19-4bda-afa5-031aa0ae13bb",
"metadata": {},
"outputs": [
@ -186,7 +190,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"I'm sorry, but as an AI language model, I don't have information about the future events. The 2022 Winter Olympics will be held in Beijing, China, from February 4 to 20, 2022. The curling events will take place during the games, and the winners of the gold medal in curling will be determined at that time.\n"
"As an AI language model, I don't have real-time data. However, I can provide you with general information. The gold medalists in curling at the 2022 Winter Olympics will be determined during the event. The winners will be the team that finishes in first place in the respective men's and women's curling competitions. To find out the specific gold medalists, you can check the official Olympic website or reliable news sources for the most up-to-date information.\n"
]
}
],
@ -194,7 +198,7 @@
"# an example question about the 2022 Olympics\n",
"query = 'Which athletes won the gold medal in curling at the 2022 Winter Olympics?'\n",
"\n",
"response = openai.ChatCompletion.create(\n",
"response = client.chat.completions.create(\n",
" messages=[\n",
" {'role': 'system', 'content': 'You answer questions about the 2022 Winter Olympics.'},\n",
" {'role': 'user', 'content': query},\n",
@ -203,7 +207,7 @@
" temperature=0,\n",
")\n",
"\n",
"print(response['choices'][0]['message']['content'])"
"print(response.choices[0].message.content)"
]
},
{
@ -221,7 +225,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 5,
"id": "02e7281d",
"metadata": {},
"outputs": [],
@ -532,7 +536,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 6,
"id": "fceaf665-2602-4788-bc44-9eb256a6f955",
"metadata": {},
"outputs": [
@ -540,7 +544,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"There were three events in curling at the 2022 Winter Olympics, so there were three sets of athletes who won gold medals. The gold medalists in men's curling were Sweden's Niklas Edin, Oskar Eriksson, Rasmus Wranå, Christoffer Sundgren, and Daniel Magnusson. The gold medalists in women's curling were Great Britain's Eve Muirhead, Vicky Wright, Jennifer Dodds, Hailey Duff, and Mili Smith. The gold medalists in mixed doubles curling were Italy's Stefania Constantini and Amos Mosaner.\n"
"In the men's curling event, the gold medal was won by Sweden. In the women's curling event, the gold medal was won by Great Britain. In the mixed doubles curling event, the gold medal was won by Italy.\n"
]
}
],
@ -554,7 +558,7 @@
"\n",
"Question: Which athletes won the gold medal in curling at the 2022 Winter Olympics?\"\"\"\n",
"\n",
"response = openai.ChatCompletion.create(\n",
"response = client.chat.completions.create(\n",
" messages=[\n",
" {'role': 'system', 'content': 'You answer questions about the 2022 Winter Olympics.'},\n",
" {'role': 'user', 'content': query},\n",
@ -563,7 +567,7 @@
" temperature=0,\n",
")\n",
"\n",
"print(response['choices'][0]['message']['content'])"
"print(response.choices[0].message.content)"
]
},
{
@ -596,7 +600,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 7,
"id": "46d50792",
"metadata": {},
"outputs": [],
@ -610,7 +614,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 8,
"id": "70307f8e",
"metadata": {},
"outputs": [],
@ -621,7 +625,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 9,
"id": "424162c2",
"metadata": {},
"outputs": [
@ -741,7 +745,7 @@
"[6059 rows x 2 columns]"
]
},
"execution_count": 7,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
@ -770,7 +774,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 10,
"id": "b9a8c713-c8a9-47dc-85a4-871ee1395566",
"metadata": {},
"outputs": [],
@ -783,11 +787,11 @@
" top_n: int = 100\n",
") -> tuple[list[str], list[float]]:\n",
" \"\"\"Returns a list of strings and relatednesses, sorted from most related to least.\"\"\"\n",
" query_embedding_response = openai.Embedding.create(\n",
" query_embedding_response = client.embeddings.create(\n",
" model=EMBEDDING_MODEL,\n",
" input=query,\n",
" )\n",
" query_embedding = query_embedding_response[\"data\"][0][\"embedding\"]\n",
" query_embedding = query_embedding_response.data[0].embedding\n",
" strings_and_relatednesses = [\n",
" (row[\"text\"], relatedness_fn(query_embedding, row[\"embedding\"]))\n",
" for i, row in df.iterrows()\n",
@ -799,7 +803,7 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 11,
"id": "da034bd2",
"metadata": {},
"outputs": [
@ -912,7 +916,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 12,
"id": "1f45cecc",
"metadata": {},
"outputs": [],
@ -961,12 +965,12 @@
" {\"role\": \"system\", \"content\": \"You answer questions about the 2022 Winter Olympics.\"},\n",
" {\"role\": \"user\", \"content\": message},\n",
" ]\n",
" response = openai.ChatCompletion.create(\n",
" response = client.chat.completions.create(\n",
" model=model,\n",
" messages=messages,\n",
" temperature=0\n",
" )\n",
" response_message = response[\"choices\"][0][\"message\"][\"content\"]\n",
" response_message = response.choices[0].message.content\n",
" return response_message\n",
"\n"
]
@ -984,17 +988,17 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 13,
"id": "e11f53ab",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\"There were two gold medal-winning teams in curling at the 2022 Winter Olympics: the Swedish men's team consisting of Niklas Edin, Oskar Eriksson, Rasmus Wranå, Christoffer Sundgren, and Daniel Magnusson, and the British women's team consisting of Eve Muirhead, Vicky Wright, Jennifer Dodds, Hailey Duff, and Mili Smith.\""
"\"In the men's curling tournament, the gold medal was won by the team from Sweden, consisting of Niklas Edin, Oskar Eriksson, Rasmus Wranå, Christoffer Sundgren, and Daniel Magnusson. In the women's curling tournament, the gold medal was won by the team from Great Britain, consisting of Eve Muirhead, Vicky Wright, Jennifer Dodds, Hailey Duff, and Mili Smith.\""
]
},
"execution_count": 11,
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
@ -1036,7 +1040,7 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 14,
"id": "aa965e36",
"metadata": {},
"outputs": [
@ -1274,36 +1278,6 @@
"\n",
"==Results summary==\n",
"\n",
"===Women's tournament===\n",
"\n",
"====Playoffs====\n",
"\n",
"=====Bronze medal game=====\n",
"\n",
"''Saturday, 19 February, 20:05''\n",
"{{#lst:Curling at the 2022 Winter Olympics Women's tournament|BM}}\n",
"{{Player percentages\n",
"| team1 = {{flagIOC|SUI|2022 Winter}}\n",
"| [[Melanie Barbezat]] | 79%\n",
"| [[Esther Neuenschwander]] | 75%\n",
"| [[Silvana Tirinzoni]] | 81%\n",
"| [[Alina Pätz]] | 64%\n",
"| teampct1 = 75%\n",
"| team2 = {{flagIOC|SWE|2022 Winter}}\n",
"| [[Sofia Mabergs]] | 89%\n",
"| [[Agnes Knochenhauer]] | 80%\n",
"| [[Sara McManus]] | 81%\n",
"| [[Anna Hasselborg]] | 76%\n",
"| teampct2 = 82%\n",
"}}\n",
"\"\"\"\n",
"\n",
"Wikipedia article section:\n",
"\"\"\"\n",
"Curling at the 2022 Winter Olympics\n",
"\n",
"==Results summary==\n",
"\n",
"===Mixed doubles tournament===\n",
"\n",
"====Playoffs====\n",
@ -1329,16 +1303,46 @@
"|}\n",
"\"\"\"\n",
"\n",
"Wikipedia article section:\n",
"\"\"\"\n",
"Curling at the 2022 Winter Olympics\n",
"\n",
"==Results summary==\n",
"\n",
"===Women's tournament===\n",
"\n",
"====Playoffs====\n",
"\n",
"=====Bronze medal game=====\n",
"\n",
"''Saturday, 19 February, 20:05''\n",
"{{#lst:Curling at the 2022 Winter Olympics Women's tournament|BM}}\n",
"{{Player percentages\n",
"| team1 = {{flagIOC|SUI|2022 Winter}}\n",
"| [[Melanie Barbezat]] | 79%\n",
"| [[Esther Neuenschwander]] | 75%\n",
"| [[Silvana Tirinzoni]] | 81%\n",
"| [[Alina Pätz]] | 64%\n",
"| teampct1 = 75%\n",
"| team2 = {{flagIOC|SWE|2022 Winter}}\n",
"| [[Sofia Mabergs]] | 89%\n",
"| [[Agnes Knochenhauer]] | 80%\n",
"| [[Sara McManus]] | 81%\n",
"| [[Anna Hasselborg]] | 76%\n",
"| teampct2 = 82%\n",
"}}\n",
"\"\"\"\n",
"\n",
"Question: Which athletes won the gold medal in curling at the 2022 Winter Olympics?\n"
]
},
{
"data": {
"text/plain": [
"\"There were two gold medal-winning teams in curling at the 2022 Winter Olympics: the Swedish men's team consisting of Niklas Edin, Oskar Eriksson, Rasmus Wranå, Christoffer Sundgren, and Daniel Magnusson, and the British women's team consisting of Eve Muirhead, Vicky Wright, Jennifer Dodds, Hailey Duff, and Mili Smith.\""
"\"In the men's tournament, the Swedish team consisting of Niklas Edin, Oskar Eriksson, Rasmus Wranå, Christoffer Sundgren, and Daniel Magnusson won the gold medal in curling at the 2022 Winter Olympics. In the women's tournament, the British team consisting of Eve Muirhead, Vicky Wright, Jennifer Dodds, Hailey Duff, and Mili Smith won the gold medal.\""
]
},
"execution_count": 12,
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
@ -1361,17 +1365,17 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 15,
"id": "d6cb292f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\"The gold medal winners in curling at the 2022 Winter Olympics are as follows:\\n\\nMen's tournament: Team Sweden, consisting of Niklas Edin, Oskar Eriksson, Rasmus Wranå, Christoffer Sundgren, and Daniel Magnusson.\\n\\nWomen's tournament: Team Great Britain, consisting of Eve Muirhead, Vicky Wright, Jennifer Dodds, Hailey Duff, and Mili Smith.\\n\\nMixed doubles tournament: Team Italy, consisting of Stefania Constantini and Amos Mosaner.\""
"\"The athletes who won the gold medal in curling at the 2022 Winter Olympics are:\\n\\nMen's tournament: Niklas Edin, Oskar Eriksson, Rasmus Wranå, Christoffer Sundgren, and Daniel Magnusson from Sweden.\\n\\nWomen's tournament: Eve Muirhead, Vicky Wright, Jennifer Dodds, Hailey Duff, and Mili Smith from Great Britain.\\n\\nMixed doubles tournament: Stefania Constantini and Amos Mosaner from Italy.\""
]
},
"execution_count": 13,
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
@ -1402,17 +1406,17 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 16,
"id": "05fb04ef",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'A number of world records (WR) and Olympic records (OR) were set in various skating events at the 2022 Winter Olympics in Beijing, China. However, the exact number of records set is not specified in the given articles.'"
"'I could not find an answer.'"
]
},
"execution_count": 14,
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
@ -1424,17 +1428,17 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 17,
"id": "30da5271",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Jamaica had more athletes at the 2022 Winter Olympics with a total of 7 athletes (6 men and 1 woman) competing in 2 sports, while Cuba did not participate in the 2022 Winter Olympics.'"
"\"Jamaica had more athletes at the 2022 Winter Olympics. According to the provided information, Jamaica had a total of 7 athletes (6 men and 1 woman) competing in 2 sports, while there is no information about Cuba's participation in the 2022 Winter Olympics.\""
]
},
"execution_count": 15,
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
@ -1446,17 +1450,17 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 18,
"id": "42449926",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'I could not find an answer. The entertainment value of Olympic sports is subjective and varies from person to person.'"
"'I could not find an answer.'"
]
},
"execution_count": 16,
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
@ -1468,7 +1472,7 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": 19,
"id": "34e4b7e1",
"metadata": {},
"outputs": [
@ -1478,7 +1482,7 @@
"'I could not find an answer.'"
]
},
"execution_count": 17,
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
@ -1490,17 +1494,17 @@
},
{
"cell_type": "code",
"execution_count": 18,
"execution_count": 20,
"id": "57d13b1f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'With a beak so grand and wide,\\nThe Shoebill Stork glides with pride,\\nElegant in every stride,\\nA true beauty of the wild.'"
"'I could not find an answer.'"
]
},
"execution_count": 18,
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
@ -1512,17 +1516,17 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 21,
"id": "f997e261",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'I could not find an answer.'"
"\"In the marsh, the Shoebill stands tall and stark,\\nWith a grace that lights up the day's dark.\\nIts elegance in flight, a breathtaking art,\\nA living masterpiece, nature's work of heart.\""
]
},
"execution_count": 19,
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
@ -1534,17 +1538,17 @@
},
{
"cell_type": "code",
"execution_count": 20,
"execution_count": 22,
"id": "0d3dad92",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\"There were multiple gold medalists in curling at the 2022 Winter Olympics. The women's team from Great Britain and the men's team from Sweden both won gold medals in their respective tournaments.\""
"\"According to the provided information, the gold medal winners in curling at the 2022 Winter Olympics were:\\n\\n- Men's tournament: Sweden (Niklas Edin, Oskar Eriksson, Rasmus Wranå, Christoffer Sundgren, Daniel Magnusson)\\n- Women's tournament: Great Britain (Eve Muirhead, Vicky Wright, Jennifer Dodds, Hailey Duff, Mili Smith)\\n- Mixed doubles tournament: Italy (Stefania Constantini, Amos Mosaner)\""
]
},
"execution_count": 20,
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
@ -1556,7 +1560,7 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 23,
"id": "afa3b95f",
"metadata": {},
"outputs": [
@ -1566,7 +1570,7 @@
"'I could not find an answer.'"
]
},
"execution_count": 21,
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
@ -1578,17 +1582,17 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 24,
"id": "627e131e",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'I could not find an answer. This question is not related to the provided articles on the 2022 Winter Olympics.'"
"'I could not find an answer.'"
]
},
"execution_count": 22,
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
@ -1600,17 +1604,17 @@
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": 25,
"id": "c5aad00d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\"The COVID-19 pandemic had a significant impact on the 2022 Winter Olympics. The qualifying process for some sports was changed due to the cancellation of tournaments in 2020, and all athletes were required to remain within a bio-secure bubble for the duration of their participation, which included daily COVID-19 testing. Only residents of the People's Republic of China were permitted to attend the Games as spectators, and ticket sales to the general public were canceled. Some top athletes, considered to be medal contenders, were not able to travel to China after having tested positive, even if asymptomatic. There were also complaints from athletes and team officials about the quarantine facilities and conditions they faced. Additionally, there were 437 total coronavirus cases detected and reported by the Beijing Organizing Committee since January 23, 2022.\""
"'COVID-19 had several impacts on the 2022 Winter Olympics. Here are some of the effects:\\n\\n1. Changes in Qualification: The qualifying process for curling and women\\'s ice hockey had to be altered due to the cancellation of tournaments in 2020. Qualification for curling was based on placement in the 2021 World Curling Championships and an Olympic Qualification Event. The women\\'s tournament qualification was based on existing IIHF World Rankings.\\n\\n2. Biosecurity Protocols: The International Olympic Committee (IOC) announced biosecurity protocols for the Games, which included a \"closed-loop management system\" where athletes had to remain within a bio-secure bubble. Athletes were required to undergo daily COVID-19 testing and could only travel to and from Games-related venues. Only residents of China were allowed to attend the Games as spectators.\\n\\n3. NHL Player Withdrawal: The National Hockey League (NHL) and National Hockey League Players\\' Association (NHLPA) announced that NHL players would not participate in the men\\'s hockey tournament due to concerns over COVID-19 and the need to make up postponed games.\\n\\n4. Limited Spectators: Ticket sales to the general public were canceled, and only limited numbers of spectators were admitted by invitation only. The Games were closed to the general public, with spectators only present at events held in Beijing and Zhangjiakou.\\n\\n5. Use of My2022 App: Everyone present at the Games, including athletes, staff, and attendees, were required to use the My2022 mobile app as part of the biosecurity protocols. The app was used for health reporting, COVID-19 vaccination and testing records, customs declarations, and messaging.\\n\\n6. Athlete Absences: Some top athletes, including Austrian ski jumper Marita Kramer and Russian skeletonist Nikita Tregubov, were unable to travel to China after testing positive for COVID-19, even if asymptomatic.\\n\\n7. COVID-19 Cases: There were a total of 437 COVID-19 cases linked to the 2022 Winter Olympics, with 171 cases among the COVID-19 protective bubble residents and the rest detected through airport testing of games-related arrivals.\\n\\nPlease note that this answer is based on the provided articles and may not include all possible impacts of COVID-19 on the 2022 Winter Olympics.'"
]
},
"execution_count": 23,
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
@ -1637,7 +1641,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.9"
"version": "3.10.5"
},
"vscode": {
"interpreter": {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -14,14 +14,14 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ada-002 embedding performance on 1k Amazon reviews: mse=0.62, mae=0.53\n"
"text-embedding-3-small performance on 1k Amazon reviews: mse=0.65, mae=0.52\n"
]
}
],
@ -48,12 +48,12 @@
"mse = mean_squared_error(y_test, preds)\n",
"mae = mean_absolute_error(y_test, preds)\n",
"\n",
"print(f\"ada-002 embedding performance on 1k Amazon reviews: mse={mse:.2f}, mae={mae:.2f}\")\n"
"print(f\"text-embedding-3-small performance on 1k Amazon reviews: mse={mse:.2f}, mae={mae:.2f}\")\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"metadata": {},
"outputs": [
{
@ -105,7 +105,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.9"
"version": "3.11.5"
},
"orig_nbformat": 4,
"vscode": {

File diff suppressed because one or more lines are too long

@ -54,9 +54,14 @@
"import arxiv\n",
"from math import exp\n",
"import openai\n",
"import os\n",
"import pandas as pd\n",
"from tenacity import retry, wait_random_exponential, stop_after_attempt\n",
"import tiktoken"
"import tiktoken\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n",
"\n",
"OPENAI_MODEL = \"gpt-4\""
]
},
{
@ -202,7 +207,7 @@
],
"source": [
"tokens = [\" Yes\", \" No\"]\n",
"tokenizer = tiktoken.encoding_for_model(\"text-davinci-003\")\n",
"tokenizer = tiktoken.encoding_for_model(OPENAI_MODEL)\n",
"ids = [tokenizer.encode(token) for token in tokens]\n",
"ids[0], ids[1]"
]
@ -245,19 +250,19 @@
"\n",
"@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3))\n",
"def document_relevance(query, document):\n",
" response = openai.Completion.create(\n",
" response = openai.chat.completions.create(\n",
" model=\"text-davinci-003\",\n",
" prompt=prompt.format(query=query, document=content),\n",
" message=prompt.format(query=query, document=document),\n",
" temperature=0,\n",
" logprobs=1,\n",
" logprobs=True,\n",
" logit_bias={3363: 1, 1400: 1},\n",
" )\n",
"\n",
" return (\n",
" query,\n",
" document,\n",
" response[\"choices\"][0][\"text\"],\n",
" response[\"choices\"][0][\"logprobs\"][\"token_logprobs\"][0],\n",
" response.choices[0].message.content,\n",
" response.choices[0].logprobs.token_logprobs[0],\n",
" )"
]
},
@ -271,8 +276,8 @@
"content = result_list[0][\"title\"] + \": \" + result_list[0][\"summary\"]\n",
"\n",
"# Set logprobs to 1 so our response will include the most probable token the model identified\n",
"response = openai.Completion.create(\n",
" model=\"text-davinci-003\",\n",
"response = openai.chat.completions.create(\n",
" model=OPENAI_MODEL,\n",
" prompt=prompt.format(query=query, document=content),\n",
" temperature=0,\n",
" logprobs=1,\n",
@ -317,9 +322,9 @@
}
],
"source": [
"result = response[\"choices\"][0]\n",
"print(f\"Result was {result['text']}\")\n",
"print(f\"Logprobs was {result['logprobs']['token_logprobs'][0]}\")\n",
"result = response.choices[0]\n",
"print(f\"Result was {result.message.content}\")\n",
"print(f\"Logprobs was {result.logprobs.token_logprobs[0]}\")\n",
"print(\"\\nBelow is the full logprobs object\\n\\n\")\n",
"print(result[\"logprobs\"])"
]

@ -12,7 +12,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@ -36,18 +36,18 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"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",
"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",
"\n",
"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",
"Fantastic Instant Refried beans: Fantastic Instant Refried Beans have been a staple for my family now for nearly 20 years. All 7 of us love it and my grown kids are passing on the tradition.\n",
"\n",
"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",
"Delicious: While there may be better coffee beans available, this is my first purchase and my first time grinding my own beans. I read several reviews before purchasing this brand, and am extremely \n",
"\n"
]
}
@ -59,7 +59,7 @@
"def search_reviews(df, product_description, n=3, pprint=True):\n",
" product_embedding = get_embedding(\n",
" product_description,\n",
" model=\"text-embedding-ada-002\"\n",
" model=\"text-embedding-3-small\"\n",
" )\n",
" df[\"similarity\"] = df.embedding.apply(lambda x: cosine_similarity(x, product_embedding))\n",
"\n",
@ -81,7 +81,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 4,
"metadata": {},
"outputs": [
{
@ -92,7 +92,7 @@
"\n",
"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",
"\n",
"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",
"Bland and vaguely gamy tasting, skip this one: As far as prepared dinner kits go, \"Barilla Whole Grain Mezze Penne with Tomato and Basil Sauce\" just did not do it for me...and this is coming from a p\n",
"\n"
]
}
@ -111,7 +111,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 5,
"metadata": {},
"outputs": [
{
@ -137,14 +137,14 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Extremely dissapointed: Hi,<br />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.<br /><br />Thanks.<b\n",
"Disappointed: The metal cover has severely disformed. And most of the cookies inside have been crushed into small pieces. Shopping experience is awful. I'll never buy it online again.\n",
"\n"
]
}
@ -155,16 +155,16 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Good food: The only dry food my queen cat will eat. Helps prevent hair balls. Good packaging. Arrives promptly. Recommended by a friend who sells pet food.\n",
"Great food!: I wanted a food for a a dog with skin problems. His skin greatly improved with the switch, though he still itches some. He loves the food. No recalls, American made with American ingred\n",
"\n",
"The cats like it: My 7 cats like this food but it is a little yucky for the human. Pieces of mackerel swimming in a dark broth. It is billed as a \"complete\" food and contains carrots, peas and pasta.\n",
"Great food!: I wanted a food for a a dog with skin problems. His skin greatly improved with the switch, though he still itches some. He loves the food. No recalls, American made with American ingred\n",
"\n"
]
}
@ -190,7 +190,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.9"
"version": "3.11.5"
},
"orig_nbformat": 4,
"vscode": {

@ -26,13 +26,16 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"# imports needed to run the code in this notebook\n",
"import ast # used for detecting whether generated Python code is valid\n",
"import openai # used for calling the OpenAI API\n",
"import os\n",
"from openai import OpenAI\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n",
"\n",
"color_prefix_by_role = {\n",
" \"system\": \"\\033[0m\", # gray\n",
@ -96,19 +99,17 @@
" if print_text:\n",
" print_messages(explain_messages)\n",
"\n",
" explanation_response = openai.ChatCompletion.create(\n",
" model=explain_model,\n",
" messages=explain_messages,\n",
" temperature=temperature,\n",
" stream=True,\n",
" )\n",
" explanation_response = client.chat.completions.create(model=explain_model,\n",
" messages=explain_messages,\n",
" temperature=temperature,\n",
" stream=True)\n",
" explanation = \"\"\n",
" for chunk in explanation_response:\n",
" delta = chunk[\"choices\"][0][\"delta\"]\n",
" delta = chunk.choices[0].delta\n",
" if print_text:\n",
" print_message_delta(delta)\n",
" if \"content\" in delta:\n",
" explanation += delta[\"content\"]\n",
" explanation += delta.content\n",
" explain_assistant_message = {\"role\": \"assistant\", \"content\": explanation}\n",
"\n",
" # Step 2: Generate a plan to write a unit test\n",
@ -133,19 +134,17 @@
" ]\n",
" if print_text:\n",
" print_messages([plan_user_message])\n",
" plan_response = openai.ChatCompletion.create(\n",
" model=plan_model,\n",
" messages=plan_messages,\n",
" temperature=temperature,\n",
" stream=True,\n",
" )\n",
" plan_response = client.chat.completions.create(model=plan_model,\n",
" messages=plan_messages,\n",
" temperature=temperature,\n",
" stream=True)\n",
" plan = \"\"\n",
" for chunk in plan_response:\n",
" delta = chunk[\"choices\"][0][\"delta\"]\n",
" delta = chunk.choices[0].delta\n",
" if print_text:\n",
" print_message_delta(delta)\n",
" if \"content\" in delta:\n",
" plan += delta[\"content\"]\n",
" explanation += delta.content\n",
" plan_assistant_message = {\"role\": \"assistant\", \"content\": plan}\n",
"\n",
" # Step 2b: If the plan is short, ask GPT to elaborate further\n",
@ -167,19 +166,17 @@
" ]\n",
" if print_text:\n",
" print_messages([elaboration_user_message])\n",
" elaboration_response = openai.ChatCompletion.create(\n",
" model=plan_model,\n",
" messages=elaboration_messages,\n",
" temperature=temperature,\n",
" stream=True,\n",
" )\n",
" elaboration_response = client.chat.completions.create(model=plan_model,\n",
" messages=elaboration_messages,\n",
" temperature=temperature,\n",
" stream=True)\n",
" elaboration = \"\"\n",
" for chunk in elaboration_response:\n",
" delta = chunk[\"choices\"][0][\"delta\"]\n",
" if print_text:\n",
" print_message_delta(delta)\n",
" if \"content\" in delta:\n",
" elaboration += delta[\"content\"]\n",
" delta = chunk.choices[0].delta\n",
" if print_text:\n",
" print_message_delta(delta)\n",
" if \"content\" in delta:\n",
" explanation += delta.content\n",
" elaboration_assistant_message = {\"role\": \"assistant\", \"content\": elaboration}\n",
"\n",
" # Step 3: Generate the unit test\n",
@ -222,19 +219,17 @@
" if print_text:\n",
" print_messages([execute_system_message, execute_user_message])\n",
"\n",
" execute_response = openai.ChatCompletion.create(\n",
" model=execute_model,\n",
" execute_response = client.chat.completions.create(model=execute_model,\n",
" messages=execute_messages,\n",
" temperature=temperature,\n",
" stream=True,\n",
" )\n",
" stream=True)\n",
" execution = \"\"\n",
" for chunk in execute_response:\n",
" delta = chunk[\"choices\"][0][\"delta\"]\n",
" delta = chunk.choices[0].delta\n",
" if print_text:\n",
" print_message_delta(delta)\n",
" if \"content\" in delta:\n",
" execution += delta[\"content\"]\n",
" if delta.content:\n",
" execution += delta.content\n",
"\n",
" # check the output for errors\n",
" code = execution.split(\"```python\")[1].split(\"```\")[0].strip()\n",
@ -263,7 +258,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 12,
"metadata": {},
"outputs": [
{
@ -297,18 +292,7 @@
" return ' '.join(translated_words)\n",
"\n",
"```\n",
"\u001b[92m\n",
"[assistant]\n",
"The `pig_latin` function takes a string of text and returns the text translated into pig latin. Here's how it works:\n",
"\n",
"* The function defines a nested function called `translate` that takes a single word as input and returns the word translated into pig latin.\n",
"* The `translate` function first defines a string of vowels.\n",
"* If the first letter of the input word is a vowel, the function adds \"way\" to the end of the word and returns the result.\n",
"* If the first letter of the input word is a consonant, the function loops through the word's letters until it finds a vowel.\n",
"* The function then takes the consonants at the beginning of the word and moves them to the end of the word, adding \"ay\" to the end of the word.\n",
"* The `pig_latin` function lowercases the input text and splits it into a list of words.\n",
"* The function then applies the `translate` function to each word in the list using a list comprehension.\n",
"* Finally, the function joins the translated words back together into a single string with spaces between each word and returns the result.\u001b[0m\n",
"\u001b[0m\n",
"[user]\n",
"A good unit test suite should aim to:\n",
"- Test the function's behavior for a wide range of possible inputs\n",
@ -318,50 +302,10 @@
"- Be deterministic, so that the tests always pass or fail in the same way\n",
"\n",
"To help unit test the function above, list diverse scenarios that the function should be able to handle (and under each scenario, include a few examples as sub-bullets).\n",
"\u001b[92m\n",
"[assistant]\n",
"Here are some scenarios that the `pig_latin` function should be able to handle, along with examples:\n",
"\n",
"* Words that start with a vowel:\n",
" * \"apple\" -> \"appleway\"\n",
" * \"elephant\" -> \"elephantway\"\n",
"* Words that start with a single consonant:\n",
" * \"pig\" -> \"igpay\"\n",
" * \"latin\" -> \"atinlay\"\n",
"* Words that start with multiple consonants:\n",
" * \"string\" -> \"ingstray\"\n",
" * \"glove\" -> \"oveglay\"\n",
"* Words that contain numbers or special characters:\n",
" * \"hello!\" -> \"ellohay!\"\n",
" * \"world123\" -> \"orldway123\"\n",
"* Sentences with multiple words:\n",
" * \"hello world\" -> \"ellohay orldway\"\n",
" * \"the quick brown fox\" -> \"hetay ickquay ownbray oxfay\"\n",
"* Sentences with punctuation:\n",
" * \"Hello, world!\" -> \"Ellohay, orldway!\"\n",
" * \"The quick brown fox...\" -> \"Hetay ickquay ownbray oxfay...\" \n",
"* Empty strings:\n",
" * \"\" -> \"\"\u001b[0m\n",
"\u001b[0m\n",
"[user]\n",
"In addition to those scenarios above, list a few rare or unexpected edge cases (and as before, under each edge case, include a few examples as sub-bullets).\n",
"\u001b[92m\n",
"[assistant]\n",
"Here are some rare or unexpected edge cases that the `pig_latin` function should be able to handle, along with examples:\n",
"\n",
"* Words that consist entirely of consonants:\n",
" * \"xyz\" -> \"xyzay\"\n",
" * \"rhythm\" -> \"ythmrhay\"\n",
"* Words that consist entirely of vowels:\n",
" * \"aeiou\" -> \"aeiouway\"\n",
" * \"ouiea\" -> \"ouieaway\"\n",
"* Words that contain mixed case:\n",
" * \"PyThOn\" -> \"ythonpay\"\n",
" * \"eLePhAnT\" -> \"elephantway\"\n",
"* Strings that contain only whitespace:\n",
" * \" \" -> \"\"\n",
"* Non-string inputs:\n",
" * None -> TypeError\n",
" * 42 -> AttributeError\u001b[0m\n",
"\u001b[0m\n",
"[system]\n",
"You are a world-class Python developer with an eagle eye for unintended bugs and edge cases. You write careful, accurate unit tests. When asked to reply only with code, you write all of your code in a single block.\n",
"\u001b[0m\n",
@ -397,74 +341,7 @@
"# below, each test case is represented by a tuple passed to the @pytest.mark.parametrize decorator\n",
"{insert unit test code here}\n",
"```\n",
"\u001b[92m\n",
"[assistant]\n",
"```python\n",
"# imports\n",
"import pytest\n",
"\n",
"# function to test\n",
"def pig_latin(text):\n",
" def translate(word):\n",
" vowels = 'aeiou'\n",
" if word[0] in vowels:\n",
" return word + 'way'\n",
" else:\n",
" consonants = ''\n",
" for letter in word:\n",
" if letter not in vowels:\n",
" consonants += letter\n",
" else:\n",
" break\n",
" return word[len(consonants):] + consonants + 'ay'\n",
"\n",
" words = text.lower().split()\n",
" translated_words = [translate(word) for word in words]\n",
" return ' '.join(translated_words)\n",
"\n",
"\n",
"# unit tests\n",
"# below, each test case is represented by a tuple passed to the @pytest.mark.parametrize decorator\n",
"\n",
"# Tests for normal cases\n",
"@pytest.mark.parametrize('text, expected', [\n",
" ('apple', 'appleway'),\n",
" ('elephant', 'elephantway'),\n",
" ('pig', 'igpay'),\n",
" ('latin', 'atinlay'),\n",
" ('string', 'ingstray'),\n",
" ('glove', 'oveglay'),\n",
" ('hello world', 'ellohay orldway'),\n",
" ('the quick brown fox', 'hetay ickquay ownbray oxfay'),\n",
" ('Hello, world!', 'Ellohay, orldway!'),\n",
" ('The quick brown fox...', 'Hetay ickquay ownbray oxfay...'),\n",
" ('', ''),\n",
"])\n",
"\n",
"def test_pig_latin_normal_cases(text, expected):\n",
" assert pig_latin(text) == expected\n",
"\n",
"\n",
"# Tests for edge cases\n",
"@pytest.mark.parametrize('text, expected', [\n",
" ('xyz', 'xyzay'),\n",
" ('rhythm', 'ythmrhay'),\n",
" ('aeiou', 'aeiouway'),\n",
" ('ouiea', 'ouieaway'),\n",
" ('PyThOn', 'ythonpay'),\n",
" ('eLePhAnT', 'elephantway'),\n",
" (' ', ''),\n",
" (None, TypeError),\n",
" (42, AttributeError)\n",
"])\n",
"\n",
"def test_pig_latin_edge_cases(text, expected):\n",
" if type(expected) == type:\n",
" with pytest.raises(expected):\n",
" pig_latin(text)\n",
" else:\n",
" assert pig_latin(text) == expected\n",
"```"
"execute messages: [{'role': 'system', 'content': 'You are a world-class Python developer with an eagle eye for unintended bugs and edge cases. You write careful, accurate unit tests. When asked to reply only with code, you write all of your code in a single block.'}, {'role': 'user', 'content': \"Please explain the following Python function. Review what each element of the function is doing precisely and what the author's intentions may have been. Organize your explanation as a markdown-formatted, bulleted list.\\n\\n```python\\ndef pig_latin(text):\\n def translate(word):\\n vowels = 'aeiou'\\n if word[0] in vowels:\\n return word + 'way'\\n else:\\n consonants = ''\\n for letter in word:\\n if letter not in vowels:\\n consonants += letter\\n else:\\n break\\n return word[len(consonants):] + consonants + 'ay'\\n\\n words = text.lower().split()\\n translated_words = [translate(word) for word in words]\\n return ' '.join(translated_words)\\n\\n```\"}, {'role': 'assistant', 'content': ''}, {'role': 'user', 'content': \"A good unit test suite should aim to:\\n- Test the function's behavior for a wide range of possible inputs\\n- Test edge cases that the author may not have foreseen\\n- Take advantage of the features of `pytest` to make the tests easy to write and maintain\\n- Be easy to read and understand, with clean code and descriptive names\\n- Be deterministic, so that the tests always pass or fail in the same way\\n\\nTo help unit test the function above, list diverse scenarios that the function should be able to handle (and under each scenario, include a few examples as sub-bullets).\"}, {'role': 'assistant', 'content': ''}, {'role': 'user', 'content': 'In addition to those scenarios above, list a few rare or unexpected edge cases (and as before, under each edge case, include a few examples as sub-bullets).'}, {'role': 'assistant', 'content': ''}, {'role': 'user', 'content': \"Using Python and the `pytest` package, write a suite of unit tests for the function, following the cases above. Include helpful comments to explain each line. Reply only with code, formatted as follows:\\n\\n```python\\n# imports\\nimport pytest # used for our unit tests\\n{insert other imports as needed}\\n\\n# function to test\\ndef pig_latin(text):\\n def translate(word):\\n vowels = 'aeiou'\\n if word[0] in vowels:\\n return word + 'way'\\n else:\\n consonants = ''\\n for letter in word:\\n if letter not in vowels:\\n consonants += letter\\n else:\\n break\\n return word[len(consonants):] + consonants + 'ay'\\n\\n words = text.lower().split()\\n translated_words = [translate(word) for word in words]\\n return ' '.join(translated_words)\\n\\n\\n# unit tests\\n# below, each test case is represented by a tuple passed to the @pytest.mark.parametrize decorator\\n{insert unit test code here}\\n```\"}]\n"
]
}
],
@ -497,7 +374,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 13,
"metadata": {},
"outputs": [
{
@ -528,46 +405,19 @@
"\n",
"\n",
"# unit tests\n",
"# below, each test case is represented by a tuple passed to the @pytest.mark.parametrize decorator\n",
"\n",
"# Tests for normal cases\n",
"@pytest.mark.parametrize('text, expected', [\n",
" ('apple', 'appleway'),\n",
" ('elephant', 'elephantway'),\n",
" ('pig', 'igpay'),\n",
" ('latin', 'atinlay'),\n",
" ('string', 'ingstray'),\n",
" ('glove', 'oveglay'),\n",
" ('hello world', 'ellohay orldway'),\n",
" ('the quick brown fox', 'hetay ickquay ownbray oxfay'),\n",
" ('Hello, world!', 'Ellohay, orldway!'),\n",
" ('The quick brown fox...', 'Hetay ickquay ownbray oxfay...'),\n",
" ('', ''),\n",
" ('hello world', 'ellohay orldway'), # basic test case\n",
" ('Python is awesome', 'ythonPay isway awesomeway'), # test case with multiple words\n",
" ('apple', 'appleway'), # test case with a word starting with a vowel\n",
" ('', ''), # test case with an empty string\n",
" ('123', '123'), # test case with non-alphabetic characters\n",
" ('Hello World!', 'elloHay orldWay!'), # test case with punctuation\n",
" ('The quick brown fox', 'ethay ickquay ownbray oxfay'), # test case with mixed case words\n",
" ('a e i o u', 'away eway iway oway uway'), # test case with all vowels\n",
" ('bcd fgh jkl mnp', 'bcday fghay jklway mnpay'), # test case with all consonants\n",
"])\n",
"\n",
"def test_pig_latin_normal_cases(text, expected):\n",
" assert pig_latin(text) == expected\n",
"\n",
"\n",
"# Tests for edge cases\n",
"@pytest.mark.parametrize('text, expected', [\n",
" ('xyz', 'xyzay'),\n",
" ('rhythm', 'ythmrhay'),\n",
" ('aeiou', 'aeiouway'),\n",
" ('ouiea', 'ouieaway'),\n",
" ('PyThOn', 'ythonpay'),\n",
" ('eLePhAnT', 'elephantway'),\n",
" (' ', ''),\n",
" (None, TypeError),\n",
" (42, AttributeError)\n",
"])\n",
"\n",
"def test_pig_latin_edge_cases(text, expected):\n",
" if type(expected) == type:\n",
" with pytest.raises(expected):\n",
" pig_latin(text)\n",
" else:\n",
" assert pig_latin(text) == expected\n"
"def test_pig_latin(text, expected):\n",
" assert pig_latin(text) == expected\n"
]
}
],
@ -600,7 +450,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.9"
"version": "3.11.3"
},
"orig_nbformat": 4,
"vscode": {

@ -18,7 +18,7 @@
"The code example illustrates a few optional embellishments on the chained, multi-step prompt:\n",
"\n",
"- Conditional branching (e.g., only asking for elaboration if the first plan is too short)\n",
"- Different models for different steps (e.g., `text-davinci-002` for the text planning steps and `code-davinci-002` for the code writing step)\n",
"- Different models for different steps (e.g., `gpt-3.5-turbo-instruct` for the text planning steps and `gpt-4` for the code writing step)\n",
"- A check that re-runs the function if the output is unsatisfactory (e.g., if the output code cannot be parsed by Python's `ast` module)\n",
"- Streaming output so that you can start reading the output before it's fully generated (useful for long, multi-step outputs)\n",
"\n",
@ -67,9 +67,8 @@
"metadata": {},
"outputs": [],
"source": [
"# imports needed to run the code in this notebook\n",
"import ast # used for detecting whether generated Python code is valid\n",
"import openai # used for calling the OpenAI API\n",
"import openai\n",
"\n",
"# example of a function that uses a multi-step prompt to write unit tests\n",
"def unit_test_from_function(\n",
@ -77,8 +76,8 @@
" unit_test_package: str = \"pytest\", # unit testing package; use the name as it appears in the import statement\n",
" approx_min_cases_to_cover: int = 7, # minimum number of test case categories to cover (approximate)\n",
" print_text: bool = False, # optionally prints text; helpful for understanding the function & debugging\n",
" text_model: str = \"text-davinci-002\", # model used to generate text plans in steps 1, 2, and 2b\n",
" code_model: str = \"code-davinci-002\", # if you don't have access to code models, you can use text models here instead\n",
" text_model: str = \"gpt-3.5-turbo-instruct\", # model used to generate text plans in steps 1, 2, and 2b\n",
" code_model: str = \"gpt-3.5-turbo-instruct\", # if you don't have access to code models, you can use text models here instead\n",
" max_tokens: int = 1000, # can set this high, as generations should be stopped earlier by stop sequences\n",
" temperature: float = 0.4, # temperature = 0 can sometimes get stuck in repetitive loops, so we use 0.4\n",
" reruns_if_fail: int = 1, # if the output code cannot be parsed, this will re-run the function up to N times\n",

File diff suppressed because one or more lines are too long

@ -7,7 +7,7 @@
"source": [
"## Using embeddings\n",
"\n",
"This notebook contains some helpful snippets you can use to embed text with the 'text-embedding-ada-002' model via the OpenAI API."
"This notebook contains some helpful snippets you can use to embed text with the `text-embedding-3-small` model via the OpenAI API."
]
},
{
@ -30,7 +30,7 @@
"import openai\n",
"\n",
"embedding = openai.Embedding.create(\n",
" input=\"Your text goes here\", model=\"text-embedding-ada-002\"\n",
" input=\"Your text goes here\", model=\"text-embedding-3-small\"\n",
")[\"data\"][0][\"embedding\"]\n",
"len(embedding)\n"
]
@ -55,7 +55,7 @@
"num_embeddings = 10000 # Some large number\n",
"for i in range(num_embeddings):\n",
" embedding = openai.Embedding.create(\n",
" input=\"Your text goes here\", model=\"text-embedding-ada-002\"\n",
" input=\"Your text goes here\", model=\"text-embedding-3-small\"\n",
" )[\"data\"][0][\"embedding\"]\n",
" print(len(embedding))"
]
@ -80,10 +80,10 @@
"\n",
"# Retry up to 6 times with exponential backoff, starting at 1 second and maxing out at 20 seconds delay\n",
"@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))\n",
"def get_embedding(text: str, model=\"text-embedding-ada-002\") -> list[float]:\n",
"def get_embedding(text: str, model=\"text-embedding-3-small\") -> list[float]:\n",
" return openai.Embedding.create(input=[text], model=model)[\"data\"][0][\"embedding\"]\n",
"\n",
"embedding = get_embedding(\"Your text goes here\", model=\"text-embedding-ada-002\")\n",
"embedding = get_embedding(\"Your text goes here\", model=\"text-embedding-3-small\")\n",
"print(len(embedding))"
]
}

@ -0,0 +1,812 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Using logprobs for classification and Q&A evaluation\n",
"\n",
"This notebook demonstrates the use of the `logprobs` parameter in the Chat Completions API. When `logprobs` is enabled, the API returns the log probabilities of each output token, along with a limited number of the most likely tokens at each token position and their log probabilities. The relevant request parameters are:\n",
"* `logprobs`: Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the content of message. This option is currently not available on the `gpt-4-vision-preview` model.\n",
"* `top_logprobs`: An integer between 0 and 5 specifying the number of most likely tokens to return at each token position, each with an associated log probability. `logprobs` must be set to true if this parameter is used.\n",
"\n",
"Log probabilities of output tokens indicate the likelihood of each token occurring in the sequence given the context. To simplify, a logprob is `log(p)`, where `p` = probability of a token occurring at a specific position based on the previous tokens in the context. Some key points about `logprobs`:\n",
"* Higher log probabilities suggest a higher likelihood of the token in that context. This allows users to gauge the model's confidence in its output or explore alternative responses the model considered.\n",
"* Logprob can be any negative number or `0.0`. `0.0` corresponds to 100% probability.\n",
"* Logprobs allow us to compute the joint probability of a sequence as the sum of the logprobs of the individual tokens. This is useful for scoring and ranking model outputs. Another common approach is to take the average per-token logprob of a sentence to choose the best generation.\n",
"* We can examine the `logprobs` assigned to different candidate tokens to understand what options the model considered plausible or implausible.\n",
"\n",
"While there are a wide array of use cases for `logprobs`, this notebook will focus on its use for:\n",
"\n",
"1. Classification tasks\n",
"\n",
"* Large Language Models excel at many classification tasks, but accurately measuring the model's confidence in its outputs can be challenging. `logprobs` provide a probability associated with each class prediction, enabling users to set their own classification or confidence thresholds.\n",
"\n",
"2. Retrieval (Q&A) evaluation\n",
"\n",
"* `logprobs` can assist with self-evaluation in retrieval applications. In the Q&A example, the model outputs a contrived `has_sufficient_context_for_answer` boolean, which can serve as a confidence score of whether the answer is contained in the retrieved content. Evaluations of this type can reduce retrieval-based hallucinations and enhance accuracy.\n",
"\n",
"3. Autocomplete\n",
"* `logprobs` could help us decide how to suggest words as a user is typing.\n",
"\n",
"4. Token highlighting and outputting bytes\\n\",\n",
"* Users can easily create a token highlighter using the built in tokenization that comes with enabling `logprobs`. Additionally, the bytes parameter includes the ASCII encoding of each output character, which is particularly useful for reproducing emojis and special characters.\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 0. Imports and utils"
]
},
{
"cell_type": "code",
"execution_count": 264,
"metadata": {},
"outputs": [],
"source": [
"from openai import OpenAI\n",
"from math import exp\n",
"import numpy as np\n",
"from IPython.display import display, HTML\n",
"import os\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))"
]
},
{
"cell_type": "code",
"execution_count": 265,
"metadata": {},
"outputs": [],
"source": [
"def get_completion(\n",
" messages: list[dict[str, str]],\n",
" model: str = \"gpt-4\",\n",
" max_tokens=500,\n",
" temperature=0,\n",
" stop=None,\n",
" seed=123,\n",
" tools=None,\n",
" logprobs=None, # whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the content of message..\n",
" top_logprobs=None,\n",
") -> str:\n",
" params = {\n",
" \"model\": model,\n",
" \"messages\": messages,\n",
" \"max_tokens\": max_tokens,\n",
" \"temperature\": temperature,\n",
" \"stop\": stop,\n",
" \"seed\": seed,\n",
" \"logprobs\": logprobs,\n",
" \"top_logprobs\": top_logprobs,\n",
" }\n",
" if tools:\n",
" params[\"tools\"] = tools\n",
"\n",
" completion = client.chat.completions.create(**params)\n",
" return completion"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. Using `logprobs` to assess confidence for classification tasks"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's say we want to create a system to classify news articles into a set of pre-defined categories. Without `logprobs`, we can use Chat Completions to do this, but it is much more difficult to assess the certainty with which the model made its classifications.\n",
"\n",
"Now, with `logprobs` enabled, we can see exactly how confident the model is in its predictions, which is crucial for creating an accurate and trustworthy classifier. For example, if the log probability for the chosen category is high, this suggests the model is quite confident in its classification. If it's low, this suggests the model is less confident. This can be particularly useful in cases where the model's classification is not what you expected, or when the model's output needs to be reviewed or validated by a human."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We'll begin with a prompt that presents the model with four categories: **Technology, Politics, Sports, and Arts**. The model is then tasked with classifying articles into these categories based solely on their headlines."
]
},
{
"cell_type": "code",
"execution_count": 266,
"metadata": {},
"outputs": [],
"source": [
"CLASSIFICATION_PROMPT = \"\"\"You will be given a headline of a news article.\n",
"Classify the article into one of the following categories: Technology, Politics, Sports, and Art.\n",
"Return only the name of the category, and nothing else.\n",
"MAKE SURE your output is one of the four categories stated.\n",
"Article headline: {headline}\"\"\"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's look at three sample headlines, and first begin with a standard Chat Completions output, without `logprobs`"
]
},
{
"cell_type": "code",
"execution_count": 267,
"metadata": {},
"outputs": [],
"source": [
"headlines = [\n",
" \"Tech Giant Unveils Latest Smartphone Model with Advanced Photo-Editing Features.\",\n",
" \"Local Mayor Launches Initiative to Enhance Urban Public Transport.\",\n",
" \"Tennis Champion Showcases Hidden Talents in Symphony Orchestra Debut\",\n",
"]\n"
]
},
{
"cell_type": "code",
"execution_count": 268,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Headline: Tech Giant Unveils Latest Smartphone Model with Advanced Photo-Editing Features.\n",
"Category: Technology\n",
"\n",
"\n",
"Headline: Local Mayor Launches Initiative to Enhance Urban Public Transport.\n",
"Category: Politics\n",
"\n",
"\n",
"Headline: Tennis Champion Showcases Hidden Talents in Symphony Orchestra Debut\n",
"Category: Art\n",
"\n"
]
}
],
"source": [
"for headline in headlines:\n",
" print(f\"\\nHeadline: {headline}\")\n",
" API_RESPONSE = get_completion(\n",
" [{\"role\": \"user\", \"content\": CLASSIFICATION_PROMPT.format(headline=headline)}],\n",
" model=\"gpt-4\",\n",
" )\n",
" print(f\"Category: {API_RESPONSE.choices[0].message.content}\\n\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here we can see the selected category for each headline. However, we have no visibility into the confidence of the model in its predictions. Let's rerun the same prompt but with `logprobs` enabled, and `top_logprobs` set to 2 (this will show us the 2 most likely output tokens for each token). Additionally we can also output the linear probability of each output token, in order to convert the log probability to the more easily interprable scale of 0-100%. "
]
},
{
"cell_type": "code",
"execution_count": 269,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Headline: Tech Giant Unveils Latest Smartphone Model with Advanced Photo-Editing Features.\n"
]
},
{
"data": {
"text/html": [
"<span style='color: cyan'>Output token 1:</span> Technology, <span style='color: darkorange'>logprobs:</span> -2.4584822e-06, <span style='color: magenta'>linear probability:</span> 100.0%<br><span style='color: cyan'>Output token 2:</span> Techn, <span style='color: darkorange'>logprobs:</span> -13.781253, <span style='color: magenta'>linear probability:</span> 0.0%<br>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\n",
"Headline: Local Mayor Launches Initiative to Enhance Urban Public Transport.\n"
]
},
{
"data": {
"text/html": [
"<span style='color: cyan'>Output token 1:</span> Politics, <span style='color: darkorange'>logprobs:</span> -2.4584822e-06, <span style='color: magenta'>linear probability:</span> 100.0%<br><span style='color: cyan'>Output token 2:</span> Technology, <span style='color: darkorange'>logprobs:</span> -13.937503, <span style='color: magenta'>linear probability:</span> 0.0%<br>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\n",
"Headline: Tennis Champion Showcases Hidden Talents in Symphony Orchestra Debut\n"
]
},
{
"data": {
"text/html": [
"<span style='color: cyan'>Output token 1:</span> Art, <span style='color: darkorange'>logprobs:</span> -0.009169078, <span style='color: magenta'>linear probability:</span> 99.09%<br><span style='color: cyan'>Output token 2:</span> Sports, <span style='color: darkorange'>logprobs:</span> -4.696669, <span style='color: magenta'>linear probability:</span> 0.91%<br>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n"
]
}
],
"source": [
"for headline in headlines:\n",
" print(f\"\\nHeadline: {headline}\")\n",
" API_RESPONSE = get_completion(\n",
" [{\"role\": \"user\", \"content\": CLASSIFICATION_PROMPT.format(headline=headline)}],\n",
" model=\"gpt-4\",\n",
" logprobs=True,\n",
" top_logprobs=2,\n",
" )\n",
" top_two_logprobs = API_RESPONSE.choices[0].logprobs.content[0].top_logprobs\n",
" html_content = \"\"\n",
" for i, logprob in enumerate(top_two_logprobs, start=1):\n",
" html_content += (\n",
" f\"<span style='color: cyan'>Output token {i}:</span> {logprob.token}, \"\n",
" f\"<span style='color: darkorange'>logprobs:</span> {logprob.logprob}, \"\n",
" f\"<span style='color: magenta'>linear probability:</span> {np.round(np.exp(logprob.logprob)*100,2)}%<br>\"\n",
" )\n",
" display(HTML(html_content))\n",
" print(\"\\n\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As expected from the first two headlines, `gpt-4` is nearly 100% confident in its classifications, as the content is clearly technology and politics focused respectively. However, the third headline combines both sports and art-related themes, so we see the model is less confident in its selection.\n",
"\n",
"This shows how important using `logprobs` can be, as if we are using LLMs for classification tasks we can set confidence theshholds, or output several potential output tokens if the log probability of the selected output is not sufficiently high. For instance, if we are creating a recommendation engine to tag articles, we can automatically classify headlines crossing a certain threshold, and send the less certain headlines for manual review."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. Retrieval confidence scoring to reduce hallucinations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To reduce hallucinations, and the performance of our RAG-based Q&A system, we can use `logprobs` to evaluate how confident the model is in its retrieval."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's say we have built a retrieval system using RAG for Q&A, but are struggling with hallucinated answers to our questions. *Note:* we will use a hardcoded article for this example, but see other entries in the cookbook for tutorials on using RAG for Q&A."
]
},
{
"cell_type": "code",
"execution_count": 270,
"metadata": {},
"outputs": [],
"source": [
"# Article retrieved\n",
"ada_lovelace_article = \"\"\"Augusta Ada King, Countess of Lovelace (née Byron; 10 December 1815 27 November 1852) was an English mathematician and writer, chiefly known for her work on Charles Babbage's proposed mechanical general-purpose computer, the Analytical Engine. She was the first to recognise that the machine had applications beyond pure calculation.\n",
"Ada Byron was the only legitimate child of poet Lord Byron and reformer Lady Byron. All Lovelace's half-siblings, Lord Byron's other children, were born out of wedlock to other women. Byron separated from his wife a month after Ada was born and left England forever. He died in Greece when Ada was eight. Her mother was anxious about her upbringing and promoted Ada's interest in mathematics and logic in an effort to prevent her from developing her father's perceived insanity. Despite this, Ada remained interested in him, naming her two sons Byron and Gordon. Upon her death, she was buried next to him at her request. Although often ill in her childhood, Ada pursued her studies assiduously. She married William King in 1835. King was made Earl of Lovelace in 1838, Ada thereby becoming Countess of Lovelace.\n",
"Her educational and social exploits brought her into contact with scientists such as Andrew Crosse, Charles Babbage, Sir David Brewster, Charles Wheatstone, Michael Faraday, and the author Charles Dickens, contacts which she used to further her education. Ada described her approach as \"poetical science\" and herself as an \"Analyst (& Metaphysician)\".\n",
"When she was eighteen, her mathematical talents led her to a long working relationship and friendship with fellow British mathematician Charles Babbage, who is known as \"the father of computers\". She was in particular interested in Babbage's work on the Analytical Engine. Lovelace first met him in June 1833, through their mutual friend, and her private tutor, Mary Somerville.\n",
"Between 1842 and 1843, Ada translated an article by the military engineer Luigi Menabrea (later Prime Minister of Italy) about the Analytical Engine, supplementing it with an elaborate set of seven notes, simply called \"Notes\".\n",
"Lovelace's notes are important in the early history of computers, especially since the seventh one contained what many consider to be the first computer program—that is, an algorithm designed to be carried out by a machine. Other historians reject this perspective and point out that Babbage's personal notes from the years 1836/1837 contain the first programs for the engine. She also developed a vision of the capability of computers to go beyond mere calculating or number-crunching, while many others, including Babbage himself, focused only on those capabilities. Her mindset of \"poetical science\" led her to ask questions about the Analytical Engine (as shown in her notes) examining how individuals and society relate to technology as a collaborative tool.\n",
"\"\"\"\n",
"\n",
"# Questions that can be easily answered given the article\n",
"easy_questions = [\n",
" \"What nationality was Ada Lovelace?\",\n",
" \"What was an important finding from Lovelace's seventh note?\",\n",
"]\n",
"\n",
"# Questions that are not fully covered in the article\n",
"medium_questions = [\n",
" \"Did Lovelace collaborate with Charles Dickens\",\n",
" \"What concepts did Lovelace build with Charles Babbage\",\n",
"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, what we can do is ask the model to respond to the question, but then also evaluate its response. Specifically, we will ask the model to output a boolean `has_sufficient_context_for_answer`. We can then evaluate the `logprobs` to see just how confident the model is that its answer was contained in the provided context"
]
},
{
"cell_type": "code",
"execution_count": 271,
"metadata": {},
"outputs": [],
"source": [
"PROMPT = \"\"\"You retrieved this article: {article}. The question is: {question}.\n",
"Before even answering the question, consider whether you have sufficient information in the article to answer the question fully.\n",
"Your output should JUST be the boolean true or false, of if you have sufficient information in the article to answer the question.\n",
"Respond with just one word, the boolean true or false. You must output the word 'True', or the word 'False', nothing else.\n",
"\"\"\"\n"
]
},
{
"cell_type": "code",
"execution_count": 272,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"Questions clearly answered in article<p style=\"color:green\">Question: What nationality was Ada Lovelace?</p><p style=\"color:cyan\">has_sufficient_context_for_answer: True, <span style=\"color:darkorange\">logprobs: -3.1281633e-07, <span style=\"color:magenta\">linear probability: 100.0%</span></p><p style=\"color:green\">Question: What was an important finding from Lovelace's seventh note?</p><p style=\"color:cyan\">has_sufficient_context_for_answer: True, <span style=\"color:darkorange\">logprobs: -7.89631e-07, <span style=\"color:magenta\">linear probability: 100.0%</span></p>Questions only partially covered in the article<p style=\"color:green\">Question: Did Lovelace collaborate with Charles Dickens</p><p style=\"color:cyan\">has_sufficient_context_for_answer: True, <span style=\"color:darkorange\">logprobs: -0.06993677, <span style=\"color:magenta\">linear probability: 93.25%</span></p><p style=\"color:green\">Question: What concepts did Lovelace build with Charles Babbage</p><p style=\"color:cyan\">has_sufficient_context_for_answer: False, <span style=\"color:darkorange\">logprobs: -0.61807257, <span style=\"color:magenta\">linear probability: 53.9%</span></p>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"html_output = \"\"\n",
"html_output += \"Questions clearly answered in article\"\n",
"\n",
"for question in easy_questions:\n",
" API_RESPONSE = get_completion(\n",
" [\n",
" {\n",
" \"role\": \"user\",\n",
" \"content\": PROMPT.format(\n",
" article=ada_lovelace_article, question=question\n",
" ),\n",
" }\n",
" ],\n",
" model=\"gpt-4\",\n",
" logprobs=True,\n",
" )\n",
" html_output += f'<p style=\"color:green\">Question: {question}</p>'\n",
" for logprob in API_RESPONSE.choices[0].logprobs.content:\n",
" html_output += f'<p style=\"color:cyan\">has_sufficient_context_for_answer: {logprob.token}, <span style=\"color:darkorange\">logprobs: {logprob.logprob}, <span style=\"color:magenta\">linear probability: {np.round(np.exp(logprob.logprob)*100,2)}%</span></p>'\n",
"\n",
"html_output += \"Questions only partially covered in the article\"\n",
"\n",
"for question in medium_questions:\n",
" API_RESPONSE = get_completion(\n",
" [\n",
" {\n",
" \"role\": \"user\",\n",
" \"content\": PROMPT.format(\n",
" article=ada_lovelace_article, question=question\n",
" ),\n",
" }\n",
" ],\n",
" model=\"gpt-4\",\n",
" logprobs=True,\n",
" top_logprobs=3,\n",
" )\n",
" html_output += f'<p style=\"color:green\">Question: {question}</p>'\n",
" for logprob in API_RESPONSE.choices[0].logprobs.content:\n",
" html_output += f'<p style=\"color:cyan\">has_sufficient_context_for_answer: {logprob.token}, <span style=\"color:darkorange\">logprobs: {logprob.logprob}, <span style=\"color:magenta\">linear probability: {np.round(np.exp(logprob.logprob)*100,2)}%</span></p>'\n",
"\n",
"display(HTML(html_output))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For the first two questions, our model asserts with (near) 100% confidence that the article has sufficient context to answer the posed questions.<br><br>\n",
"On the other hand, for the more tricky questions which are less clearly answered in the article, the model is less confident that it has sufficient context. This is a great guardrail to help ensure our retrieved content is sufficient.<br><br>\n",
"This self-evaluation can help reduce hallucinations, as you can restrict answers or re-prompt the user when your `sufficient_context_for_answer` log probability is below a certain threshold. Methods like this have been shown to significantly reduce RAG for Q&A hallucinations and errors ([Example]((https://jfan001.medium.com/how-we-cut-the-rate-of-gpt-hallucinations-from-20-to-less-than-2-f3bfcc10e4ec)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. Autocomplete"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Another use case for `logprobs` are autocomplete systems. Without creating the entire autocomplete system end-to-end, let's demonstrate how `logprobs` could help us decide how to suggest words as a user is typing."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First, let's come up with a sample sentence: `\"My least favorite TV show is Breaking Bad.\"` Let's say we want it to dynamically recommend the next word or token as we are typing the sentence, but *only* if the model is quite sure of what the next word will be. To demonstrate this, let's break up the sentence into sequential components."
]
},
{
"cell_type": "code",
"execution_count": 273,
"metadata": {},
"outputs": [],
"source": [
"sentence_list = [\n",
" \"My\",\n",
" \"My least\",\n",
" \"My least favorite\",\n",
" \"My least favorite TV\",\n",
" \"My least favorite TV show\",\n",
" \"My least favorite TV show is\",\n",
" \"My least favorite TV show is Breaking Bad\",\n",
"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we can ask `gpt-3.5-turbo` to act as an autocomplete engine with whatever context the model is given. We can enable `logprobs` and can see how confident the model is in its prediction."
]
},
{
"cell_type": "code",
"execution_count": 274,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<p>Sentence: My</p><p style=\"color:cyan\">Predicted next token: favorite, <span style=\"color:darkorange\">logprobs: -0.18245785, <span style=\"color:magenta\">linear probability: 83.32%</span></p><p style=\"color:cyan\">Predicted next token: dog, <span style=\"color:darkorange\">logprobs: -2.397172, <span style=\"color:magenta\">linear probability: 9.1%</span></p><p style=\"color:cyan\">Predicted next token: ap, <span style=\"color:darkorange\">logprobs: -3.8732424, <span style=\"color:magenta\">linear probability: 2.08%</span></p><br><p>Sentence: My least</p><p style=\"color:cyan\">Predicted next token: favorite, <span style=\"color:darkorange\">logprobs: -0.0146376295, <span style=\"color:magenta\">linear probability: 98.55%</span></p><p style=\"color:cyan\">Predicted next token: My, <span style=\"color:darkorange\">logprobs: -4.2417912, <span style=\"color:magenta\">linear probability: 1.44%</span></p><p style=\"color:cyan\">Predicted next token: favorite, <span style=\"color:darkorange\">logprobs: -9.748788, <span style=\"color:magenta\">linear probability: 0.01%</span></p><br><p>Sentence: My least favorite</p><p style=\"color:cyan\">Predicted next token: food, <span style=\"color:darkorange\">logprobs: -0.9481721, <span style=\"color:magenta\">linear probability: 38.74%</span></p><p style=\"color:cyan\">Predicted next token: My, <span style=\"color:darkorange\">logprobs: -1.3447137, <span style=\"color:magenta\">linear probability: 26.06%</span></p><p style=\"color:cyan\">Predicted next token: color, <span style=\"color:darkorange\">logprobs: -1.3887696, <span style=\"color:magenta\">linear probability: 24.94%</span></p><br><p>Sentence: My least favorite TV</p><p style=\"color:cyan\">Predicted next token: show, <span style=\"color:darkorange\">logprobs: -0.0007898556, <span style=\"color:magenta\">linear probability: 99.92%</span></p><p style=\"color:cyan\">Predicted next token: My, <span style=\"color:darkorange\">logprobs: -7.711523, <span style=\"color:magenta\">linear probability: 0.04%</span></p><p style=\"color:cyan\">Predicted next token: series, <span style=\"color:darkorange\">logprobs: -9.348547, <span style=\"color:magenta\">linear probability: 0.01%</span></p><br><p>Sentence: My least favorite TV show</p><p style=\"color:cyan\">Predicted next token: is, <span style=\"color:darkorange\">logprobs: -0.2851253, <span style=\"color:magenta\">linear probability: 75.19%</span></p><p style=\"color:cyan\">Predicted next token: of, <span style=\"color:darkorange\">logprobs: -1.55335, <span style=\"color:magenta\">linear probability: 21.15%</span></p><p style=\"color:cyan\">Predicted next token: My, <span style=\"color:darkorange\">logprobs: -3.4928775, <span style=\"color:magenta\">linear probability: 3.04%</span></p><br><p>Sentence: My least favorite TV show is</p><p style=\"color:cyan\">Predicted next token: \"My, <span style=\"color:darkorange\">logprobs: -0.69349754, <span style=\"color:magenta\">linear probability: 49.98%</span></p><p style=\"color:cyan\">Predicted next token: \"The, <span style=\"color:darkorange\">logprobs: -1.2899293, <span style=\"color:magenta\">linear probability: 27.53%</span></p><p style=\"color:cyan\">Predicted next token: My, <span style=\"color:darkorange\">logprobs: -2.4170141, <span style=\"color:magenta\">linear probability: 8.92%</span></p><br><p>Sentence: My least favorite TV show is Breaking Bad</p><p style=\"color:cyan\">Predicted next token: because, <span style=\"color:darkorange\">logprobs: -0.17786823, <span style=\"color:magenta\">linear probability: 83.71%</span></p><p style=\"color:cyan\">Predicted next token: ,, <span style=\"color:darkorange\">logprobs: -2.3946173, <span style=\"color:magenta\">linear probability: 9.12%</span></p><p style=\"color:cyan\">Predicted next token: ., <span style=\"color:darkorange\">logprobs: -3.1861975, <span style=\"color:magenta\">linear probability: 4.13%</span></p><br>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"high_prob_completions = {}\n",
"low_prob_completions = {}\n",
"html_output = \"\"\n",
"\n",
"for sentence in sentence_list:\n",
" PROMPT = \"\"\"Complete this sentence. You are acting as auto-complete. Simply complete the sentence to the best of your ability, make sure it is just ONE sentence: {sentence}\"\"\"\n",
" API_RESPONSE = get_completion(\n",
" [{\"role\": \"user\", \"content\": PROMPT.format(sentence=sentence)}],\n",
" model=\"gpt-3.5-turbo\",\n",
" logprobs=True,\n",
" top_logprobs=3,\n",
" )\n",
" html_output += f'<p>Sentence: {sentence}</p>'\n",
" first_token = True\n",
" for token in API_RESPONSE.choices[0].logprobs.content[0].top_logprobs:\n",
" html_output += f'<p style=\"color:cyan\">Predicted next token: {token.token}, <span style=\"color:darkorange\">logprobs: {token.logprob}, <span style=\"color:magenta\">linear probability: {np.round(np.exp(token.logprob)*100,2)}%</span></p>'\n",
" if first_token:\n",
" if np.exp(token.logprob) > 0.95:\n",
" high_prob_completions[sentence] = token.token\n",
" if np.exp(token.logprob) < 0.60:\n",
" low_prob_completions[sentence] = token.token\n",
" first_token = False\n",
" html_output += \"<br>\"\n",
"\n",
"display(HTML(html_output))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's look at the high confidence autocompletions:"
]
},
{
"cell_type": "code",
"execution_count": 275,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'My least': 'favorite', 'My least favorite TV': 'show'}"
]
},
"execution_count": 275,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"high_prob_completions\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"These look reasonable! We can feel confident in those suggestions. It's pretty likely you want to write 'show' after writing 'My least favorite TV'! Now let's look at the autocompletion suggestions the model was less confident about:"
]
},
{
"cell_type": "code",
"execution_count": 276,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'My least favorite': 'food', 'My least favorite TV show is': '\"My'}"
]
},
"execution_count": 276,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"low_prob_completions\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"These are logical as well. It's pretty unclear what the user is going to say with just the prefix 'my least favorite', and it's really anyone's guess what the author's favorite TV show is. <br><br>\n",
"So, using `gpt-3.5-turbo`, we can create the root of a dynamic autocompletion engine with `logprobs`!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. Highlighter and bytes parameter"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's quickly touch on creating a simple token highlighter with `logprobs`, and using the bytes parameter. First, we can create a function that counts and highlights each token. While this doesn't use the log probabilities, it uses the built in tokenization that comes with enabling `logprobs`."
]
},
{
"cell_type": "code",
"execution_count": 277,
"metadata": {},
"outputs": [],
"source": [
"PROMPT = \"\"\"What's the longest word in the English language?\"\"\"\n",
"\n",
"API_RESPONSE = get_completion(\n",
" [{\"role\": \"user\", \"content\": PROMPT}], model=\"gpt-4\", logprobs=True, top_logprobs=5\n",
")\n",
"\n",
"\n",
"def highlight_text(api_response):\n",
" colors = [\n",
" \"#FF00FF\", # Magenta\n",
" \"#008000\", # Green\n",
" \"#FF8C00\", # Dark Orange\n",
" \"#FF0000\", # Red\n",
" \"#0000FF\", # Blue\n",
" ]\n",
" tokens = api_response.choices[0].logprobs.content\n",
"\n",
" color_idx = 0 # Initialize color index\n",
" html_output = \"\" # Initialize HTML output\n",
" for t in tokens:\n",
" token_str = bytes(t.bytes).decode(\"utf-8\") # Decode bytes to string\n",
"\n",
" # Add colored token to HTML output\n",
" html_output += f\"<span style='color: {colors[color_idx]}'>{token_str}</span>\"\n",
"\n",
" # Move to the next color\n",
" color_idx = (color_idx + 1) % len(colors)\n",
" display(HTML(html_output)) # Display HTML output\n",
" print(f\"Total number of tokens: {len(tokens)}\")"
]
},
{
"cell_type": "code",
"execution_count": 278,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<span style='color: #FF00FF'>The</span><span style='color: #008000'> longest</span><span style='color: #FF8C00'> word</span><span style='color: #FF0000'> in</span><span style='color: #0000FF'> the</span><span style='color: #FF00FF'> English</span><span style='color: #008000'> language</span><span style='color: #FF8C00'>,</span><span style='color: #FF0000'> according</span><span style='color: #0000FF'> to</span><span style='color: #FF00FF'> the</span><span style='color: #008000'> Guinness</span><span style='color: #FF8C00'> World</span><span style='color: #FF0000'> Records</span><span style='color: #0000FF'>,</span><span style='color: #FF00FF'> is</span><span style='color: #008000'> '</span><span style='color: #FF8C00'>p</span><span style='color: #FF0000'>ne</span><span style='color: #0000FF'>um</span><span style='color: #FF00FF'>on</span><span style='color: #008000'>oul</span><span style='color: #FF8C00'>tram</span><span style='color: #FF0000'>icro</span><span style='color: #0000FF'>sc</span><span style='color: #FF00FF'>op</span><span style='color: #008000'>ics</span><span style='color: #FF8C00'>il</span><span style='color: #FF0000'>ic</span><span style='color: #0000FF'>ov</span><span style='color: #FF00FF'>ol</span><span style='color: #008000'>cano</span><span style='color: #FF8C00'>con</span><span style='color: #FF0000'>iosis</span><span style='color: #0000FF'>'.</span><span style='color: #FF00FF'> It</span><span style='color: #008000'> is</span><span style='color: #FF8C00'> a</span><span style='color: #FF0000'> type</span><span style='color: #0000FF'> of</span><span style='color: #FF00FF'> lung</span><span style='color: #008000'> disease</span><span style='color: #FF8C00'> caused</span><span style='color: #FF0000'> by</span><span style='color: #0000FF'> inh</span><span style='color: #FF00FF'>aling</span><span style='color: #008000'> ash</span><span style='color: #FF8C00'> and</span><span style='color: #FF0000'> sand</span><span style='color: #0000FF'> dust</span><span style='color: #FF00FF'>.</span>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Total number of tokens: 51\n"
]
}
],
"source": [
"highlight_text(API_RESPONSE)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, let's reconstruct a sentence using the bytes parameter. With `logprobs` enabled, we are given both each token and the ASCII (decimal utf-8) values of the token string. These ASCII values can be helpful when handling tokens of or containing emojis or special characters."
]
},
{
"cell_type": "code",
"execution_count": 279,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Token: \\xf0\\x9f\\x92\n",
"Log prob: -0.0003056686\n",
"Linear prob: 99.97 %\n",
"Bytes: [240, 159, 146] \n",
"\n",
"Token: \\x99\n",
"Log prob: 0.0\n",
"Linear prob: 100.0 %\n",
"Bytes: [153] \n",
"\n",
"Token: -\n",
"Log prob: -0.0096905725\n",
"Linear prob: 99.04 %\n",
"Bytes: [32, 45] \n",
"\n",
"Token: Blue\n",
"Log prob: -0.00042042506\n",
"Linear prob: 99.96 %\n",
"Bytes: [32, 66, 108, 117, 101] \n",
"\n",
"Token: Heart\n",
"Log prob: -7.302705e-05\n",
"Linear prob: 99.99 %\n",
"Bytes: [32, 72, 101, 97, 114, 116] \n",
"\n",
"Bytes array: [240, 159, 146, 153, 32, 45, 32, 66, 108, 117, 101, 32, 72, 101, 97, 114, 116]\n",
"Decoded bytes: 💙 - Blue Heart\n",
"Joint prob: 98.96 %\n"
]
}
],
"source": [
"PROMPT = \"\"\"Output the blue heart emoji and its name.\"\"\"\n",
"API_RESPONSE = get_completion(\n",
" [{\"role\": \"user\", \"content\": PROMPT}], model=\"gpt-4\", logprobs=True\n",
")\n",
"\n",
"aggregated_bytes = []\n",
"joint_logprob = 0.0\n",
"\n",
"# Iterate over tokens, aggregate bytes and calculate joint logprob\n",
"for token in API_RESPONSE.choices[0].logprobs.content:\n",
" print(\"Token:\", token.token)\n",
" print(\"Log prob:\", token.logprob)\n",
" print(\"Linear prob:\", np.round(exp(token.logprob) * 100, 2), \"%\")\n",
" print(\"Bytes:\", token.bytes, \"\\n\")\n",
" aggregated_bytes += token.bytes\n",
" joint_logprob += token.logprob\n",
"\n",
"# Decode the aggregated bytes to text\n",
"aggregated_text = bytes(aggregated_bytes).decode(\"utf-8\")\n",
"\n",
"# Assert that the decoded text is the same as the message content\n",
"assert API_RESPONSE.choices[0].message.content == aggregated_text\n",
"\n",
"# Print the results\n",
"print(\"Bytes array:\", aggregated_bytes)\n",
"print(f\"Decoded bytes: {aggregated_text}\")\n",
"print(\"Joint prob:\", np.round(exp(joint_logprob) * 100, 2), \"%\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here, we see that while the first token was `\\xf0\\x9f\\x92'`, we can get its ASCII value and append it to a bytes array. Then, we can easily decode this array into a full sentence, and validate with our assert statement that the decoded bytes is the same as our completion message!\n",
"\n",
"Additionally, we can get the joint probability of the entire completion, which is the exponentiated product of each token's log probability. This gives us how `likely` this given completion is given the prompt. Since, our prompt is quite directive (asking for a certain emoji and its name), the joint probability of this output is high! If we ask for a random output however, we'll see a much lower joint probability. This can also be a good tactic for developers during prompt engineering. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. Conclusion"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Nice! We were able to use the `logprobs` parameter to build a more robust classifier, evaluate our retrieval for Q&A system, and encode and decode each 'byte' of our tokens! `logprobs` adds useful information and signal to our completions output, and we are excited to see how developers incorporate it to improve applications."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. Possible extensions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are many other use cases for `logprobs` that are not covered in this cookbook. We can use `logprobs` for:\n",
" - Evaluations (e.g.: calculate `perplexity` of outputs, which is the evaluation metric of uncertainty or surprise of the model at its outcomes)\n",
" - Moderation\n",
" - Keyword selection\n",
" - Improve prompts and interpretability of outputs\n",
" - Token healing\n",
" - and more!"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "openai",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.16"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

File diff suppressed because one or more lines are too long

@ -138,7 +138,7 @@
"source": [
"from utils.embeddings_utils import get_embeddings\n",
"# NOTE: The following code will send a query of batch size 200 to /embeddings\n",
"matrix = get_embeddings(samples[\"text\"].to_list(), model=\"text-embedding-ada-002\")\n"
"matrix = get_embeddings(samples[\"text\"].to_list(), model=\"text-embedding-3-small\")\n"
]
},
{

@ -32,22 +32,25 @@
"outputs": [],
"source": [
"# imports\n",
"import openai # for making OpenAI API calls\n",
"import urllib # for downloading example audio files\n"
"from openai import OpenAI # for making OpenAI API calls\n",
"import urllib # for downloading example audio files\n",
"import os # for accessing environment variables\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))"
]
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('data/ZyntriQix.wav', <http.client.HTTPMessage at 0x11ac6a2b0>)"
"('data/ZyntriQix.wav', <http.client.HTTPMessage at 0x10559a910>)"
]
},
"execution_count": 2,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
@ -83,24 +86,24 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"# define a wrapper function for seeing how prompts affect transcriptions\n",
"def transcribe(prompt: str, audio_filepath) -> str:\n",
" \"\"\"Given a prompt, transcribe the audio file.\"\"\"\n",
" transcript = openai.Audio.transcribe(\n",
" transcript = client.audio.transcriptions.create(\n",
" file=open(audio_filepath, \"rb\"),\n",
" model=\"whisper-1\",\n",
" prompt=prompt,\n",
" )\n",
" return transcript[\"text\"]\n"
" return transcript.text\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 11,
"metadata": {},
"outputs": [
{
@ -109,7 +112,7 @@
"\"Have you heard of ZentricX? This tech giant boasts products like Digi-Q+, Synapse 5, VortiCore V8, Echo Nix Array, and not to forget the latest Orbital Link 7 and Digifractal Matrix. Their innovation arsenal also includes the Pulse framework, Wrapped system, they've developed a brick infrastructure court system, and launched the Flint initiative, all highlighting their commitment to relentless innovation. ZentricX, in just 30 years, has soared from a startup to a tech titan, serving us tech marvels alongside a stimulating linguistic challenge. Quite an adventure, wouldn't you agree?\""
]
},
"execution_count": 4,
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
@ -128,7 +131,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 12,
"metadata": {},
"outputs": [
{
@ -137,7 +140,7 @@
"\"Have you heard of ZyntriQix? This tech giant boasts products like Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, and not to forget the latest OrbitalLink Seven and DigiFractal Matrix. Their innovation arsenal also includes the PULSE framework, RAPT system. They've developed a B.R.I.C.K. infrastructure, Q.U.A.R.T. system, and launched the F.L.I.N.T. initiative, all highlighting their commitment to relentless innovation. ZyntriQix in just 30 years has soared from a startup to a tech titan, serving us tech marvels alongside a stimulating linguistic challenge. Quite an adventure, wouldn't you agree?\""
]
},
"execution_count": 5,
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
@ -160,7 +163,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 13,
"metadata": {},
"outputs": [
{
@ -169,7 +172,7 @@
"\"Have you heard of ZentricX? This tech giant boasts products like DigiCube Plus, Synapse 5, VortiCore V8, EchoNix Array, and not to forget the latest Orbital Link 7 and Digifractal Matrix. Their innovation arsenal also includes the PULSE framework, RAPT system. They've developed a brick infrastructure court system and launched the F.L.I.N.T. initiative, all highlighting their commitment to relentless innovation. ZentricX in just 30 years has soared from a startup to a tech titan, serving us tech marvels alongside a stimulating linguistic challenge. Quite an adventure, wouldn't you agree?\""
]
},
"execution_count": 6,
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
@ -200,13 +203,13 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"# define a wrapper function for seeing how prompts affect transcriptions\n",
"def transcribe_with_spellcheck(system_message, audio_filepath):\n",
" response = openai.ChatCompletion.create(\n",
" completion = client.chat.completions.create(\n",
" model=\"gpt-4\",\n",
" temperature=0,\n",
" messages=[\n",
@ -217,7 +220,7 @@
" },\n",
" ],\n",
" )\n",
" return response[\"choices\"][0][\"message\"][\"content\"]\n"
" return completion.choices[0].message.content\n"
]
},
{
@ -229,7 +232,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 20,
"metadata": {},
"outputs": [
{
@ -256,7 +259,7 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 21,
"metadata": {},
"outputs": [
{
@ -283,7 +286,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 22,
"metadata": {},
"outputs": [
{
@ -321,7 +324,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.9"
"version": "3.11.3"
},
"orig_nbformat": 4
},

@ -48,8 +48,7 @@
"metadata": {},
"outputs": [],
"source": [
"client = OpenAI()\n",
"client.api_key = os.getenv(\"OPENAI_API_KEY\")"
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))"
]
},
{
@ -492,7 +491,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.5"
"version": "3.11.3"
},
"orig_nbformat": 4
},

@ -43,27 +43,30 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"import openai # for making OpenAI API calls\n",
"import urllib # for downloading example audio files"
"from openai import OpenAI # for making OpenAI API calls\n",
"import urllib # for downloading example audio files\n",
"import os\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))"
]
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('data/product_names.wav', <http.client.HTTPMessage at 0x116984370>)"
"('data/product_names.wav', <http.client.HTTPMessage at 0x1105ac490>)"
]
},
"execution_count": 2,
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
@ -99,24 +102,24 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"# define a wrapper function for seeing how prompts affect transcriptions\n",
"def transcribe(audio_filepath, prompt: str) -> str:\n",
" \"\"\"Given a prompt, transcribe the audio file.\"\"\"\n",
" transcript = openai.Audio.transcribe(\n",
" transcript = client.audio.transcriptions.create(\n",
" file=open(audio_filepath, \"rb\"),\n",
" model=\"whisper-1\",\n",
" prompt=prompt,\n",
" )\n",
" return transcript[\"text\"]\n"
" return transcript.text\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 6,
"metadata": {},
"outputs": [
{
@ -125,7 +128,7 @@
"\"I stick contacts in my eyes. Do you really? Yeah. That works okay? You don't have to, like, just kind of pain in the butt every day to do that? No, it is. It is. And I sometimes just kind of miss the eye. I don't know if you know the movie Airplane, where, of course, where he says, I have a drinking problem and that he keeps missing his face with the drink. That's me and the contact lens. Surely, you must know that I know the movie Airplane. I do. I do know that. Stop calling me Shirley. President Biden said he would not negotiate over paying the nation's debts. But he is meeting today with House Speaker Kevin McCarthy. Other leaders of Congress will also attend. So how much progress can they make? I'm E. Martinez with Steve Inskeep, and this is Up First from NPR News. Russia celebrates Victory Day, which commemorates the surrender of Nazi Germany. Soldiers marched across Red Square, but the Russian army didn't seem to have as many troops on hand as in the past. So what does this ritual say about the war Russia is fighting right now?\""
]
},
"execution_count": 4,
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
@ -153,7 +156,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 7,
"metadata": {},
"outputs": [
{
@ -162,7 +165,7 @@
"\"I stick contacts in my eyes. Do you really? Yeah. That works okay? You don't have to, like, just kind of pain in the butt every day to do that? No, it is. It is. And I sometimes just kind of miss the eye. I don't know if you know the movie Airplane? Yes. Of course. Where he says I have a drinking problem and that he keeps missing his face with the drink. That's me and the contact lens. Surely, you must know that I know the movie Airplane. I do. I do know that. Don't call me Shirley. Stop calling me Shirley. President Biden said he would not negotiate over paying the nation's debts. But he is meeting today with House Speaker Kevin McCarthy. Other leaders of Congress will also attend. So how much progress can they make? I'm E. Martinez with Steve Inskeep and this is Up First from NPR News. Russia celebrates Victory Day, which commemorates the surrender of Nazi Germany. Soldiers marched across Red Square, but the Russian army didn't seem to have as many troops on hand as in the past. So what does this ritual say about the war Russia is fighting right now?\""
]
},
"execution_count": 5,
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
@ -182,7 +185,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 8,
"metadata": {},
"outputs": [
{
@ -191,7 +194,7 @@
"\"I stick contacts in my eyes. Do you really? Yeah. That works okay? You don't have to, like, just kind of pain in the butt every day to do that? No, it is. It is. And I sometimes just kind of miss the eye. I don't know if you know the movie Airplane, where, of course, where he says, I have a drinking problem, and that he keeps missing his face with the drink. That's me and the contact lens. Surely, you must know that I know the movie Airplane. I do. I do know that. Stop calling me Shirley. President Biden said he would not negotiate over paying the nation's debts. But he is meeting today with House Speaker Kevin McCarthy. Other leaders of Congress will also attend. So how much progress can they make? I'm E. Martinez with Steve Inskeep, and this is Up First from NPR News. Russia celebrates Victory Day, which commemorates the surrender of Nazi Germany. Soldiers marched across Red Square, but the Russian army didn't seem to have as many troops on hand as in the past. So what does this ritual say about the war Russia is fighting right now?\""
]
},
"execution_count": 6,
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
@ -211,7 +214,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 9,
"metadata": {},
"outputs": [
{
@ -220,7 +223,7 @@
"\"i stick contacts in my eyes. do you really? yeah. that works okay? you don't have to, like, just kind of pain in the butt? no, it is. it is. and i sometimes just kind of miss the eye. i don't know if you know, um, the movie airplane? yes. of course. where he says i have a drinking problem. and that he keeps missing his face with the drink. that's me in the contact lens. surely, you must know that i know the movie airplane. i do. i do know that. don't call me surely. stop calling me surely. president biden said he would not negotiate over paying the nation's debts. but he is meeting today with house speaker kevin mccarthy. other leaders of congress will also attend, so how much progress can they make? i'm amy martinez with steve inskeep, and this is up first from npr news. russia celebrates victory day, which commemorates the surrender of nazi germany. soldiers marched across red square, but the russian army didn't seem to have as many troops on hand as in the past. so what does this ritual say about the war russia is fighting right now?\""
]
},
"execution_count": 7,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
@ -240,7 +243,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 10,
"metadata": {},
"outputs": [
{
@ -249,7 +252,7 @@
"\"I stick contacts in my eyes. Do you really? Yeah. That works okay. You don't have to like, it's not a pain in the butt. It is. And I sometimes just kind of miss the eye. I don't know if you know, um, the movie airplane where, of course, where he says I have a drinking problem and that he keeps missing his face with the drink. That's me in the contact lens. Surely you must know that I know the movie airplane. Uh, I do. I do know that. Stop calling me Shirley. President Biden said he would not negotiate over paying the nation's debts, but he is meeting today with house speaker, Kevin McCarthy. Other leaders of Congress will also attend. So how much progress can they make? I mean, Martinez with Steve Inskeep, and this is up first from NPR news. Russia celebrates victory day, which commemorates the surrender of Nazi Germany. Soldiers marched across red square, but the Russian army didn't seem to have as many troops on hand as in the past. So what does this ritual say about the war? Russia is fighting right now.\""
]
},
"execution_count": 8,
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
@ -278,7 +281,7 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 11,
"metadata": {},
"outputs": [
{
@ -287,7 +290,7 @@
"'Welcome to Quirk, Quid, Quill, Inc., where finance meets innovation. Explore diverse offerings, from the P3 Quattro, a unique investment portfolio quadrant, to the O3 Omni, a platform for intricate derivative trading strategies. Delve into unconventional bond markets with our B3 Bond X and experience non-standard equity trading with E3 Equity. Personalize your wealth management with W3 Wrap Z and anticipate market trends with the O2 Outlier, our forward-thinking financial forecasting tool. Explore venture capital world with U3 Unifund or move your money with the M3 Mover, our sophisticated monetary transfer module. At Quirk, Quid, Quill, Inc., we turn complex finance into creative solutions. Join us in redefining financial services.'"
]
},
"execution_count": 9,
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
@ -307,7 +310,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 12,
"metadata": {},
"outputs": [
{
@ -316,7 +319,7 @@
"'Welcome to QuirkQuid Quill Inc, where finance meets innovation. Explore diverse offerings, from the P3-Quattro, a unique investment portfolio quadrant, to the O3-Omni, a platform for intricate derivative trading strategies. Delve into unconventional bond markets with our B3-BondX and experience non-standard equity trading with E3-Equity. Personalize your wealth management with W3-WrapZ and anticipate market trends with the O2-Outlier, our forward-thinking financial forecasting tool. Explore venture capital world with U3-UniFund or move your money with the M3-Mover, our sophisticated monetary transfer module. At QuirkQuid Quill Inc, we turn complex finance into creative solutions. Join us in redefining financial services.'"
]
},
"execution_count": 10,
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
@ -338,7 +341,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 13,
"metadata": {},
"outputs": [
{
@ -347,7 +350,7 @@
"\"Hello, my name is Preston Tuggle. I'm based in New York City. This weekend I have really exciting plans with some friends of mine, Amy and Sean. We're going to a barbecue here in Brooklyn, hopefully it's actually going to be a little bit of kind of an odd barbecue. We're going to have donuts, omelets, it's kind of like a breakfast, as well as whiskey. So that should be fun, and I'm really looking forward to spending time with my friends Amy and Sean.\""
]
},
"execution_count": 11,
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
@ -367,7 +370,7 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 14,
"metadata": {},
"outputs": [
{
@ -376,7 +379,7 @@
"\"Hello, my name is Preston Tuggle. I'm based in New York City. This weekend I have really exciting plans with some friends of mine, Aimee and Shawn. We're going to a barbecue here in Brooklyn. Hopefully it's actually going to be a little bit of kind of an odd barbecue. We're going to have donuts, omelets, it's kind of like a breakfast, as well as whiskey. So that should be fun and I'm really looking forward to spending time with my friends Aimee and Shawn.\""
]
},
"execution_count": 12,
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
@ -398,7 +401,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 15,
"metadata": {},
"outputs": [
{
@ -407,7 +410,7 @@
"\"Hello, my name is Preston Tuggle. I'm based in New York City. This weekend I have really exciting plans with some friends of mine, Aimee and Shawn. We're going to a barbecue here in Brooklyn. Hopefully, it's actually going to be a little bit of an odd barbecue. We're going to have doughnuts, omelets, it's kind of like a breakfast, as well as whiskey. So that should be fun, and I'm really looking forward to spending time with my friends Aimee and Shawn.\""
]
},
"execution_count": 13,
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
@ -419,7 +422,7 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 16,
"metadata": {},
"outputs": [
{
@ -428,7 +431,7 @@
"\"Hello, my name is Preston Tuggle. I'm based in New York City. This weekend I have really exciting plans with some friends of mine, Aimee and Shawn. We're going to a BBQ here in Brooklyn. Hopefully it's actually going to be a little bit of kind of an odd BBQ. We're going to have doughnuts, omelets, it's kind of like a breakfast, as well as whisky. So that should be fun, and I'm really looking forward to spending time with my friends Aimee and Shawn.\""
]
},
"execution_count": 14,
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
@ -457,7 +460,7 @@
"# define a function for GPT to generate fictitious prompts\n",
"def fictitious_prompt_from_instruction(instruction: str) -> str:\n",
" \"\"\"Given an instruction, generate a fictitious prompt.\"\"\"\n",
" response = openai.ChatCompletion.create(\n",
" response = client.chat.completions.create(\n",
" model=\"gpt-3.5-turbo-0613\",\n",
" temperature=0,\n",
" messages=[\n",
@ -468,7 +471,7 @@
" {\"role\": \"user\", \"content\": instruction},\n",
" ],\n",
" )\n",
" fictitious_prompt = response[\"choices\"][0][\"message\"][\"content\"]\n",
" fictitious_prompt = response.choices[0].message.content\n",
" return fictitious_prompt\n"
]
},
@ -569,7 +572,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.9"
"version": "3.11.3"
},
"orig_nbformat": 4
},

File diff suppressed because one or more lines are too long

@ -32,14 +32,14 @@ Inputs:
- requests_filepath : str
- path to the file containing the requests to be processed
- file should be a jsonl file, where each line is a json object with API parameters and an optional metadata field
- e.g., {"model": "text-embedding-ada-002", "input": "embed me", "metadata": {"row_id": 1}}
- e.g., {"model": "text-embedding-3-small", "input": "embed me", "metadata": {"row_id": 1}}
- as with all jsonl files, take care that newlines in the content are properly escaped (json.dumps does this automatically)
- an example file is provided at examples/data/example_requests_to_parallel_process.jsonl
- the code to generate the example file is appended to the bottom of this script
- save_filepath : str, optional
- path to the file where the results will be saved
- file will be a jsonl file, where each line is an array with the original request plus the API response
- e.g., [{"model": "text-embedding-ada-002", "input": "embed me"}, {...}]
- e.g., [{"model": "text-embedding-3-small", "input": "embed me"}, {...}]
- if omitted, results will be saved to {requests_filename}_results.jsonl
- request_url : str, optional
- URL of the API endpoint to call
@ -58,7 +58,7 @@ Inputs:
- if omitted, will default to 125,000
- token_encoding_name : str, optional
- name of the token encoding used, as defined in the `tiktoken` package
- if omitted, will default to "cl100k_base" (used by `text-embedding-ada-002`)
- if omitted, will default to "cl100k_base" (used by `text-embedding-3-small`)
- max_attempts : int, optional
- number of times to retry a failed request before giving up
- if omitted, will default to 5
@ -87,7 +87,7 @@ The script is structured as follows:
- api_endpoint_from_url (extracts API endpoint from request URL)
- append_to_jsonl (writes to results file)
- num_tokens_consumed_from_request (bigger function to infer token usage from request)
- task_id_generator_function (yields 1, 2, 3, ...)
- task_id_generator_function (yields 0, 1, 2, ...)
- Run main()
"""
@ -133,14 +133,14 @@ async def process_api_requests_from_file(
api_endpoint = api_endpoint_from_url(request_url)
request_header = {"Authorization": f"Bearer {api_key}"}
# use api-key header for Azure deployments
if '/deployments' in request_url:
if "/deployments" in request_url:
request_header = {"api-key": f"{api_key}"}
# initialize trackers
queue_of_requests_to_retry = asyncio.Queue()
task_id_generator = (
task_id_generator_function()
) # generates integer IDs of 1, 2, 3, ...
) # generates integer IDs of 0, 1, 2, ...
status_tracker = (
StatusTracker()
) # single instance to track a collection of variables
@ -371,7 +371,9 @@ def api_endpoint_from_url(request_url):
match = re.search("^https://[^/]+/v\\d+/(.+)$", request_url)
if match is None:
# for Azure OpenAI deployment urls
match = re.search(r"^https://[^/]+/openai/deployments/[^/]+/(.+?)(\?|$)", request_url)
match = re.search(
r"^https://[^/]+/openai/deployments/[^/]+/(.+?)(\?|$)", request_url
)
return match[1]
@ -488,7 +490,7 @@ if __name__ == "__main__":
"""
APPENDIX
The example requests file at openai-cookbook/examples/data/example_requests_to_parallel_process.jsonl contains 10,000 requests to text-embedding-ada-002.
The example requests file at openai-cookbook/examples/data/example_requests_to_parallel_process.jsonl contains 10,000 requests to text-embedding-3-small.
It was generated with the following code:
@ -497,7 +499,7 @@ import json
filename = "data/example_requests_to_parallel_process.jsonl"
n_requests = 10_000
jobs = [{"model": "text-embedding-ada-002", "input": str(x) + "\n"} for x in range(n_requests)]
jobs = [{"model": "text-embedding-3-small", "input": str(x) + "\n"} for x in range(n_requests)]
with open(filename, "w") as f:
for job in jobs:
json_string = json.dumps(job)

@ -174,7 +174,7 @@
"metadata": {},
"source": [
"## Deployments\n",
"In this section we are going to create a deployment using the `text-davinci-002` model that we can then use to create completions."
"In this section we are going to create a deployment using the `gpt-3.5-turbo-instruct` model that we can then use to create completions."
]
},
{
@ -183,7 +183,7 @@
"metadata": {},
"source": [
"### Deployments: Create manually\n",
"Create a new deployment by going to your Resource in your portal under \"Resource Management\" -> \"Model deployments\". Select `text-davinci-002` as the model."
"Create a new deployment by going to your Resource in your portal under \"Resource Management\" -> \"Model deployments\". Select `gpt-3.5-turbo-instruct` as the model."
]
},
{

@ -115,7 +115,6 @@
"\n",
"if use_azure_active_directory:\n",
" endpoint = os.environ[\"AZURE_OPENAI_ENDPOINT\"]\n",
" api_key = os.environ[\"AZURE_OPENAI_API_KEY\"]\n",
"\n",
" client = openai.AzureOpenAI(\n",
" azure_endpoint=endpoint,\n",

@ -9,7 +9,7 @@
"\n",
"Azure OpenAI on your data enables you to run supported chat models such as GPT-3.5-Turbo and GPT-4 on your data without needing to train or fine-tune models. Running models on your data enables you to chat on top of, and analyze your data with greater accuracy and speed. One of the key benefits of Azure OpenAI on your data is its ability to tailor the content of conversational AI. Because the model has access to, and can reference specific sources to support its responses, answers are not only based on its pretrained knowledge but also on the latest information available in the designated data source. This grounding data also helps the model avoid generating responses based on outdated or incorrect information.\n",
"\n",
"Azure OpenAI on your own data with Azure Cognitive Search provides a customizable, pre-built solution for knowledge retrieval, from which a conversational AI application can be built. To see alternative methods for knowledge retrieval and semantic search, check out the cookbook examples for [vector databases](https://github.com/openai/openai-cookbook/tree/main/examples/vector_databases)."
"Azure OpenAI on your own data with Azure AI Search (f.k.a. Azure Cognitive Search) provides a customizable, pre-built solution for knowledge retrieval, from which a conversational AI application can be built. To see alternative methods for knowledge retrieval and semantic search, check out the cookbook examples for [vector databases](https://github.com/openai/openai-cookbook/tree/main/examples/vector_databases)."
]
},
{
@ -18,7 +18,7 @@
"source": [
"## How it works\n",
"\n",
"[Azure OpenAI on your own data](https://learn.microsoft.com/azure/ai-services/openai/concepts/use-your-data) connects the model with your data, giving it the ability to retrieve and utilize data in a way that enhances the model's output. Together with Azure Cognitive Search, data is retrieved from designated data sources based on the user input and provided conversation history. The data is then augmented and resubmitted as a prompt to the model, giving the model contextual information it can use to generate a response.\n",
"[Azure OpenAI on your own data](https://learn.microsoft.com/azure/ai-services/openai/concepts/use-your-data) connects the model with your data, giving it the ability to retrieve and utilize data in a way that enhances the model's output. Together with Azure AI Search, data is retrieved from designated data sources based on the user input and provided conversation history. The data is then augmented and resubmitted as a prompt to the model, giving the model contextual information it can use to generate a response.\n",
"\n",
"See the [Data, privacy, and security for Azure OpenAI Service](https://learn.microsoft.com/legal/cognitive-services/openai/data-privacy?context=%2Fazure%2Fai-services%2Fopenai%2Fcontext%2Fcontext) for more information."
]
@ -35,7 +35,7 @@
"To use your own data with Azure OpenAI models, you will need:\n",
"\n",
"1. Azure OpenAI access and a resource with a chat model deployed (for example, GPT-3 or GPT-4)\n",
"2. Azure Cognitive Search resource\n",
"2. Azure AI Search (f.k.a. Azure Cognitive Search) resource\n",
"3. Azure Blob Storage resource\n",
"4. Your documents to be used as data (See [data source options](https://learn.microsoft.com/azure/ai-services/openai/concepts/use-your-data#data-source-options))\n",
"\n",
@ -70,8 +70,8 @@
"\n",
"* `AZURE_OPENAI_ENDPOINT` - the Azure OpenAI endpoint. This can be found under \"Keys and Endpoints\" for your Azure OpenAI resource in the Azure Portal.\n",
"* `AZURE_OPENAI_API_KEY` - the Azure OpenAI API key. This can be found under \"Keys and Endpoints\" for your Azure OpenAI resource in the Azure Portal. Omit if using Azure Active Directory authentication (see below `Authentication using Microsoft Active Directory`)\n",
"* `SEARCH_ENDPOINT` - the Cognitive Search endpoint. This URL be found on the \"Overview\" of your Search resource on the Azure Portal.\n",
"* `SEARCH_KEY` - the Cognitive Search API key. Found under \"Keys\" for your Search resource in the Azure Portal.\n",
"* `SEARCH_ENDPOINT` - the AI Search endpoint. This URL be found on the \"Overview\" of your Search resource on the Azure Portal.\n",
"* `SEARCH_KEY` - the AI Search API key. Found under \"Keys\" for your Search resource in the Azure Portal.\n",
"* `SEARCH_INDEX_NAME` - the name of the index you created with your own data."
]
},

@ -1,320 +0,0 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Azure completions example\n",
"\n",
"This example will cover completions using the Azure OpenAI service. It also includes information on content filtering."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Setup\n",
"\n",
"First, we install the necessary dependencies and import the libraries we will be using."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"! pip install \"openai>=1.0.0,<2.0.0\"\n",
"! pip install python-dotenv"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import openai\n",
"import dotenv\n",
"\n",
"dotenv.load_dotenv()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Authentication\n",
"\n",
"The Azure OpenAI service supports multiple authentication mechanisms that include API keys and Azure Active Directory token credentials."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"use_azure_active_directory = False # Set this flag to True if you are using Azure Active Directory"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Authentication using API key\n",
"\n",
"To set up the OpenAI SDK to use an *Azure API Key*, we need to set `api_key` to a key associated with your endpoint (you can find this key in *\"Keys and Endpoints\"* under *\"Resource Management\"* in the [Azure Portal](https://portal.azure.com)). You'll also find the endpoint for your resource here."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"if not use_azure_active_directory:\n",
" endpoint = os.environ[\"AZURE_OPENAI_ENDPOINT\"]\n",
" api_key = os.environ[\"AZURE_OPENAI_API_KEY\"]\n",
"\n",
" client = openai.AzureOpenAI(\n",
" azure_endpoint=endpoint,\n",
" api_key=api_key,\n",
" api_version=\"2023-09-01-preview\"\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Authentication using Azure Active Directory\n",
"Let's now see how we can autheticate via Azure Active Directory. We'll start by installing the `azure-identity` library. This library will provide the token credentials we need to authenticate and help us build a token credential provider through the `get_bearer_token_provider` helper function. It's recommended to use `get_bearer_token_provider` over providing a static token to `AzureOpenAI` because this API will automatically cache and refresh tokens for you. \n",
"\n",
"For more information on how to set up Azure Active Directory authentication with Azure OpenAI, see the [documentation](https://learn.microsoft.com/azure/ai-services/openai/how-to/managed-identity)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"! pip install \"azure-identity>=1.15.0\""
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"from azure.identity import DefaultAzureCredential, get_bearer_token_provider\n",
"\n",
"if use_azure_active_directory:\n",
" endpoint = os.environ[\"AZURE_OPENAI_ENDPOINT\"]\n",
" api_key = os.environ[\"AZURE_OPENAI_API_KEY\"]\n",
"\n",
" client = openai.AzureOpenAI(\n",
" azure_endpoint=endpoint,\n",
" azure_ad_token_provider=get_bearer_token_provider(DefaultAzureCredential(), \"https://cognitiveservices.azure.com/.default\"),\n",
" api_version=\"2023-09-01-preview\"\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> Note: the AzureOpenAI infers the following arguments from their corresponding environment variables if they are not provided:\n",
"\n",
"- `api_key` from `AZURE_OPENAI_API_KEY`\n",
"- `azure_ad_token` from `AZURE_OPENAI_AD_TOKEN`\n",
"- `api_version` from `OPENAI_API_VERSION`\n",
"- `azure_endpoint` from `AZURE_OPENAI_ENDPOINT`\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Deployments\n",
"\n",
"In this section we are going to create a deployment of a model that we can use to create completions."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Deployments: Create in the Azure OpenAI Studio\n",
"Let's deploy a model to use with completions. Go to https://portal.azure.com, find your Azure OpenAI resource, and then navigate to the Azure OpenAI Studio. Click on the \"Deployments\" tab and then create a deployment for the model you want to use for completions. The deployment name that you give the model will be used in the code below."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"deployment = \"\" # Fill in the deployment name from the portal here"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Completions\n",
"\n",
"Now let's create a completion using the client we built."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"prompt = \"The food was delicious and the waiter\"\n",
"completion = client.completions.create(\n",
" model=deployment,\n",
" prompt=prompt,\n",
" stop=\".\",\n",
" temperature=0\n",
")\n",
" \n",
"print(f\"{prompt}{completion.choices[0].text}.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Create a streaming completion\n",
"\n",
"We can also stream the response."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"prompt = \"The food was delicious and the waiter\"\n",
"response = client.completions.create(\n",
" model=deployment,\n",
" prompt=prompt,\n",
" stream=True,\n",
")\n",
"for completion in response:\n",
" if len(completion.choices) > 0:\n",
" print(f\"{completion.choices[0].text}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Content filtering\n",
"\n",
"Azure OpenAI service includes content filtering of prompts and completion responses. You can learn more about content filtering and how to configure it [here](https://learn.microsoft.com/azure/ai-services/openai/concepts/content-filter).\n",
"\n",
"If the prompt is flagged by the content filter, the library will raise a `BadRequestError` exception with a `content_filter` error code. Otherwise, you can access the `prompt_filter_results` and `content_filter_results` on the response to see the results of the content filtering and what categories were flagged."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Prompt flagged by content filter"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"\n",
"try:\n",
" completion = client.completions.create(\n",
" prompt=\"<text violating the content policy>\",\n",
" model=deployment,\n",
" )\n",
"except openai.BadRequestError as e:\n",
" err = json.loads(e.response.text)\n",
" if err[\"error\"][\"code\"] == \"content_filter\":\n",
" print(\"Content filter triggered!\")\n",
" content_filter_result = err[\"error\"][\"innererror\"][\"content_filter_result\"]\n",
" for category, details in content_filter_result.items():\n",
" print(f\"{category}:\\n filtered={details['filtered']}\\n severity={details['severity']}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Checking the result of the content filter"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"completion = client.completions.create(\n",
" prompt=\"What's the biggest city in Washington?\",\n",
" model=deployment,\n",
")\n",
"\n",
"print(f\"Answer: {completion.choices[0].text}\")\n",
"\n",
"# prompt content filter result in \"model_extra\" for azure\n",
"prompt_filter_result = completion.model_extra[\"prompt_filter_results\"][0][\"content_filter_results\"]\n",
"print(\"\\nPrompt content filter results:\")\n",
"for category, details in prompt_filter_result.items():\n",
" print(f\"{category}:\\n filtered={details['filtered']}\\n severity={details['severity']}\")\n",
"\n",
"# completion content filter result\n",
"print(\"\\nCompletion content filter results:\")\n",
"completion_filter_result = completion.choices[0].model_extra[\"content_filter_results\"]\n",
"for category, details in completion_filter_result.items():\n",
" print(f\"{category}:\\n filtered={details['filtered']}\\n severity={details['severity']}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.0"
},
"vscode": {
"interpreter": {
"hash": "3a5103089ab7e7c666b279eeded403fcec76de49a40685dbdfe9f9c78ad97c17"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}

@ -228,7 +228,7 @@
"source": [
"#### 2. Pass function definition(s) into chat completions API\n",
"\n",
"Now we can pass the function into the chat completions API. If the model determines it should call the function, a `finish_reason` of \"function_call\" will be populated on the choice and the details of which function to call and its arguments will be present in the `message`. Optionally, you can set the `function_call` keyword argument to force the model to call a particular function (e.g. `function_call={\"name\": get_current_weather}`). By default, this is set to `auto`, allowing the model to choose whether to call the function or not. "
"Now we can pass the function into the chat completions API. If the model determines it should call the function, a `finish_reason` of \"tool_calls\" will be populated on the choice and the details of which function to call and its arguments will be present in the `message`. Optionally, you can set the `tool_choice` keyword argument to force the model to call a particular function (e.g. `{\"type\": \"function\", \"function\": {\"name\": get_current_weather}}`). By default, this is set to `auto`, allowing the model to choose whether to call the function or not. "
]
},
{
@ -245,7 +245,7 @@
"chat_completion = client.chat.completions.create(\n",
" model=deployment,\n",
" messages=messages,\n",
" functions=functions,\n",
" tools=functions,\n",
")\n",
"print(chat_completion)"
]
@ -278,7 +278,7 @@
" unit = request.get(\"unit\")\n",
" return {\"temperature\": \"22\", \"unit\": \"celsius\", \"description\": \"Sunny\"}\n",
"\n",
"function_call = chat_completion.choices[0].message.function_call\n",
"function_call = chat_completion.choices[0].message.tool_calls[0].function\n",
"print(function_call.name)\n",
"print(function_call.arguments)\n",
"\n",
@ -313,7 +313,7 @@
"function_completion = client.chat.completions.create(\n",
" model=deployment,\n",
" messages=messages,\n",
" functions=functions,\n",
" tools=functions,\n",
")\n",
"\n",
"print(function_completion.choices[0].message.content.strip())"

@ -35,9 +35,12 @@
}
],
"source": [
"import openai\n",
"from openai import OpenAI\n",
"import os\n",
"from transformers import GPT2Tokenizer\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if you didn't set as an env var>\"))\n",
"\n",
"# OpenAI GPT-2 tokenizer is the same as GPT-3 tokenizer\n",
"# we use it to count the number of tokens in the text\n",
"tokenizer = GPT2Tokenizer.from_pretrained(\"gpt2\")\n",
@ -212,7 +215,7 @@
}
],
"source": [
"def translate_chunk(chunk, engine='text-davinci-002',\n",
"def translate_chunk(chunk, model='gpt-3.5-turbo',\n",
" dest_language='English',\n",
" sample_translation=(\"\\poglavje{Osnove Geometrije} \\label{osn9Geom}\", \"\\poglavje{The basics of Geometry} \\label{osn9Geom}\")\n",
" ):\n",
@ -224,17 +227,17 @@
"\n",
"{sample_translation[1]}\n",
"'''\n",
" response = openai.Completion.create(\n",
" prompt=prompt,\n",
" engine=engine,\n",
" response = client.chat.completions.create(\n",
" messages=[{\"role\": \"user\", \"content\":prompt}],\n",
" model=model,\n",
" temperature=0,\n",
" top_p=1,\n",
" max_tokens=1500,\n",
" )\n",
" result = response['choices'][0]['text'].strip()\n",
" result = response.choices[0].message.content.strip()\n",
" result = result.replace('\"\"\"', '') # remove the double quotes, as we used them to surround the text\n",
" return result\n",
"print(translate_chunk(chunks[800], engine='text-davinci-002', dest_language='English'))"
"print(translate_chunk(chunks[800], model='gpt-3.5-turbo', dest_language='English'))"
]
},
{
@ -1134,7 +1137,7 @@
"for i, chunk in enumerate(chunks):\n",
" print(str(i+1) + \" / \" + str(len(chunks)))\n",
" # translate each chunk\n",
" translated_chunks.append(translate_chunk(chunk, engine='text-davinci-002', dest_language=dest_language))\n",
" translated_chunks.append(translate_chunk(chunk, model='gpt-3.5-turbo', dest_language=dest_language))\n",
"\n",
"# join the chunks together\n",
"result = '\\n\\n'.join(translated_chunks)\n",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
{"Year":{"0":2021,"1":2021,"2":2021,"3":2021,"4":2021,"5":2021,"6":2021,"7":2021,"8":2021,"9":2021,"10":2021,"11":2021,"12":2022,"13":2022,"14":2022,"15":2022,"16":2022,"17":2022,"18":2022,"19":2022,"20":2022,"21":2022,"22":2022,"23":2022,"24":2023,"25":2023,"26":2023,"27":2023,"28":2023,"29":2023,"30":2023,"31":2023,"32":2023,"33":2023,"34":2023,"35":2023,"36":2024,"37":2024,"38":2024,"39":2024,"40":2024,"41":2024,"42":2024,"43":2024,"44":2024,"45":2024,"46":2024,"47":2024},"Quarter":{"0":"Q1","1":"Q1","2":"Q1","3":"Q2","4":"Q2","5":"Q2","6":"Q3","7":"Q3","8":"Q3","9":"Q4","10":"Q4","11":"Q4","12":"Q1","13":"Q1","14":"Q1","15":"Q2","16":"Q2","17":"Q2","18":"Q3","19":"Q3","20":"Q3","21":"Q4","22":"Q4","23":"Q4","24":"Q1","25":"Q1","26":"Q1","27":"Q2","28":"Q2","29":"Q2","30":"Q3","31":"Q3","32":"Q3","33":"Q4","34":"Q4","35":"Q4","36":"Q1","37":"Q1","38":"Q1","39":"Q2","40":"Q2","41":"Q2","42":"Q3","43":"Q3","44":"Q3","45":"Q4","46":"Q4","47":"Q4"},"Distribution channel":{"0":"Online Sales","1":"Direct Sales","2":"Retail Partners","3":"Online Sales","4":"Direct Sales","5":"Retail Partners","6":"Online Sales","7":"Direct Sales","8":"Retail Partners","9":"Online Sales","10":"Direct Sales","11":"Retail Partners","12":"Online Sales","13":"Direct Sales","14":"Retail Partners","15":"Online Sales","16":"Direct Sales","17":"Retail Partners","18":"Online Sales","19":"Direct Sales","20":"Retail Partners","21":"Online Sales","22":"Direct Sales","23":"Retail Partners","24":"Online Sales","25":"Direct Sales","26":"Retail Partners","27":"Online Sales","28":"Direct Sales","29":"Retail Partners","30":"Online Sales","31":"Direct Sales","32":"Retail Partners","33":"Online Sales","34":"Direct Sales","35":"Retail Partners","36":"Online Sales","37":"Direct Sales","38":"Retail Partners","39":"Online Sales","40":"Direct Sales","41":"Retail Partners","42":"Online Sales","43":"Direct Sales","44":"Retail Partners","45":"Online Sales","46":"Direct Sales","47":"Retail Partners"},"Revenue ($M)":{"0":1.5,"1":1.5,"2":1.5,"3":1.52,"4":1.52,"5":1.52,"6":1.54,"7":1.54,"8":1.54,"9":1.56,"10":1.56,"11":1.56,"12":1.7,"13":1.6,"14":1.55,"15":1.72,"16":1.62,"17":1.57,"18":1.74,"19":1.64,"20":1.59,"21":1.76,"22":1.66,"23":1.61,"24":1.9,"25":1.7,"26":1.6,"27":1.92,"28":1.72,"29":1.62,"30":1.94,"31":1.74,"32":1.64,"33":1.96,"34":1.76,"35":1.66,"36":2.1,"37":1.8,"38":1.65,"39":2.12,"40":1.82,"41":1.67,"42":2.14,"43":1.84,"44":1.69,"45":2.16,"46":1.86,"47":1.71},"Costs ($M)":{"0":1.3019525402,"1":1.3808087359,"2":1.3482460133,"3":1.3086075747,"4":1.4133047938,"5":1.352737358,"6":1.304110535,"7":1.41112627,"8":1.330751592,"9":1.3017953273,"10":1.4148004859,"11":1.3547054199,"12":1.296946192,"13":1.4191447337,"14":1.3544838289,"15":1.3058357645,"16":1.4119663426,"17":1.3546773599,"18":1.2975034885,"19":1.3984591745,"20":1.3677499231,"21":1.31567092,"22":1.4112211671,"23":1.357272812,"24":1.3185465104,"25":1.384730977,"26":1.364380316,"27":1.2953376608,"28":1.4055968409,"29":1.3874812782,"30":1.3116690015,"31":1.3857341315,"32":1.4179052478,"33":1.3011557968,"34":1.4177867567,"35":1.4124090189,"36":1.3027217824,"37":1.4008739329,"38":1.4566706686,"39":1.3170238655,"40":1.3965864776,"41":1.4768255148,"42":1.2828414423,"43":1.3905822245,"44":1.4784153024,"45":1.283485172,"46":1.4109693476,"47":1.4951570519},"Customer count":{"0":150,"1":151,"2":152,"3":152,"4":153,"5":154,"6":154,"7":155,"8":156,"9":156,"10":157,"11":158,"12":160,"13":161,"14":162,"15":162,"16":163,"17":164,"18":164,"19":165,"20":166,"21":166,"22":167,"23":168,"24":170,"25":171,"26":172,"27":172,"28":173,"29":174,"30":174,"31":175,"32":176,"33":176,"34":177,"35":178,"36":180,"37":181,"38":182,"39":182,"40":183,"41":184,"42":184,"43":185,"44":186,"45":186,"46":187,"47":188},"Time":{"0":"2021 Q1","1":"2021 Q1","2":"2021 Q1","3":"2021 Q2","4":"2021 Q2","5":"2021 Q2","6":"2021 Q3","7":"2021 Q3","8":"2021 Q3","9":"2021 Q4","10":"2021 Q4","11":"2021 Q4","12":"2022 Q1","13":"2022 Q1","14":"2022 Q1","15":"2022 Q2","16":"2022 Q2","17":"2022 Q2","18":"2022 Q3","19":"2022 Q3","20":"2022 Q3","21":"2022 Q4","22":"2022 Q4","23":"2022 Q4","24":"2023 Q1","25":"2023 Q1","26":"2023 Q1","27":"2023 Q2","28":"2023 Q2","29":"2023 Q2","30":"2023 Q3","31":"2023 Q3","32":"2023 Q3","33":"2023 Q4","34":"2023 Q4","35":"2023 Q4","36":"2024 Q1","37":"2024 Q1","38":"2024 Q1","39":"2024 Q2","40":"2024 Q2","41":"2024 Q2","42":"2024 Q3","43":"2024 Q3","44":"2024 Q3","45":"2024 Q4","46":"2024 Q4","47":"2024 Q4"}}

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

@ -31,7 +31,7 @@
"source": [
"**Retrieval Augmented Generation (RAG)**\n",
"\n",
"LLMs are trained on vast datasets, but these will include your specific data. Retrieval-Augmented Generation (RAG) addresses this by dynamically incorporating your data during the generation process. This is done not by altering the training data of LLMs, but by allowing the model to access and utilize your data in real-time to provide more tailored and contextually relevant responses.\n",
"LLMs are trained on vast datasets, but these will not include your specific data. Retrieval-Augmented Generation (RAG) addresses this by dynamically incorporating your data during the generation process. This is done not by altering the training data of LLMs, but by allowing the model to access and utilize your data in real-time to provide more tailored and contextually relevant responses.\n",
"\n",
"In RAG, your data is loaded and and prepared for queries or “indexed”. User queries act on the index, which filters your data down to the most relevant context. This context and your query then go to the LLM along with a prompt, and the LLM provides a response.\n",
"\n",
@ -1036,7 +1036,7 @@
"source": [
"# Let's get relevancy score\n",
"\n",
"relevancy_score = sum(result.passing for result in eval_results['faithfulness']) / len(eval_results['relevancy'])\n",
"relevancy_score = sum(result.passing for result in eval_results['relevancy']) / len(eval_results['relevancy'])\n",
"\n",
"relevancy_score\n"
]

File diff suppressed because one or more lines are too long

@ -1,4 +1,5 @@
"""
TODO: This example is deprecated.
Note: To answer questions based on text documents, we recommend the procedure in
[Question Answering using Embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Question_answering_using_embeddings.ipynb).
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).
@ -6,7 +7,10 @@ Some of the code below may rely on [deprecated API endpoints](https://github.com
import argparse
import openai
from openai import OpenAI
import os
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "<your OpenAI API key if not set as env var>"))
def create_context(
@ -21,7 +25,8 @@ def create_context(
:param max_rerank: The maximum number of reranking
:return: The context
"""
results = openai.Engine(search_model).search(
# TODO: openai.Engine(search_model) is deprecated
results = client.Engine(search_model).search(
search_model=search_model,
query=question,
max_rerank=max_rerank,
@ -80,16 +85,14 @@ def answer_question(
and fine_tuned_qa_model.split(":")[1].startswith("ft")
else {"engine": fine_tuned_qa_model}
)
response = openai.Completion.create(
prompt=f"Answer the question based on the context below\n\nText: {context}\n\n---\n\nQuestion: {question}\nAnswer:",
temperature=0,
max_tokens=max_tokens,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
stop=stop_sequence,
**model_param,
)
response = client.chat.completions.create(prompt=f"Answer the question based on the context below\n\nText: {context}\n\n---\n\nQuestion: {question}\nAnswer:",
temperature=0,
max_tokens=max_tokens,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
stop=stop_sequence,
**model_param)
return response["choices"][0]["text"]
except Exception as e:
print(e)

@ -92,7 +92,7 @@
"import time\n",
"\n",
"import pandas as pd\n",
"import openai\n",
"from openai import OpenAI\n",
"import tiktoken\n",
"import seaborn as sns\n",
"from tenacity import retry, wait_exponential\n",
@ -106,7 +106,9 @@
"import warnings\n",
"warnings.filterwarnings('ignore')\n",
"\n",
"tqdm.pandas()"
"tqdm.pandas()\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n"
]
},
{
@ -123,7 +125,6 @@
"metadata": {},
"outputs": [],
"source": [
"openai.api_key = \"sk-xxx\"\n",
"os.environ[\"QDRANT_URL\"] = \"https://xxx.cloud.qdrant.io:6333\"\n",
"os.environ[\"QDRANT_API_KEY\"] = \"xxx\""
]
@ -265,7 +266,7 @@
"# Function with tenacity for retries\n",
"@retry(wait=wait_exponential(multiplier=1, min=2, max=6))\n",
"def api_call(messages, model):\n",
" return openai.ChatCompletion.create(\n",
" return client.chat.completions.create(\n",
" model=model,\n",
" messages=messages,\n",
" stop=[\"\\n\\n\"],\n",
@ -275,10 +276,10 @@
"\n",
"\n",
"# Main function to answer question\n",
"def answer_question(row, prompt_func=get_prompt, model=\"gpt-3.5-turbo-0613\"):\n",
"def answer_question(row, prompt_func=get_prompt, model=\"gpt-3.5-turbo\"):\n",
" messages = prompt_func(row)\n",
" response = api_call(messages, model)\n",
" return response[\"choices\"][0][\"message\"][\"content\"]"
" return response.choices[0].message.content"
]
},
{
@ -557,7 +558,7 @@
" self.model_id = None\n",
"\n",
" def create_openai_file(self):\n",
" self.file_object = openai.File.create(\n",
" self.file_object = client.files.create(\n",
" file=open(self.training_file_path, \"r\"),\n",
" purpose=\"fine-tune\",\n",
" )\n",
@ -569,7 +570,7 @@
" print(\"File Status: \", self.file_object.status)\n",
"\n",
" def create_fine_tuning_job(self):\n",
" self.fine_tuning_job = openai.FineTuningJob.create(\n",
" self.fine_tuning_job = client.fine_tuning.jobs.create(\n",
" training_file=self.file_object[\"id\"],\n",
" model=self.model_name,\n",
" suffix=self.suffix,\n",
@ -582,7 +583,7 @@
" print(\"Job Status: \", self.fine_tuning_job.status)\n",
"\n",
" def retrieve_fine_tuned_model(self):\n",
" self.model_id = openai.FineTuningJob.retrieve(self.fine_tuning_job[\"id\"]).fine_tuned_model\n",
" self.model_id = client.fine_tuning.jobs.retrieve(self.fine_tuning_job[\"id\"]).fine_tuned_model\n",
" return self.model_id\n",
"\n",
" def fine_tune_model(self):\n",
@ -631,7 +632,7 @@
"metadata": {},
"outputs": [],
"source": [
"completion = openai.ChatCompletion.create(\n",
"completion = client.chat.completions.create(\n",
" model=model_id,\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n",
@ -1246,7 +1247,7 @@
"outputs": [],
"source": [
"# Let's try this out\n",
"completion = openai.ChatCompletion.create(\n",
"completion = client.chat.completions.create(\n",
" model=model_id,\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n",

@ -170,21 +170,21 @@
}
],
"source": [
"import openai\n",
"from openai import OpenAI\n",
"\n",
"client = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\", \"<your OpenAI API key if not set as env var>\"))\n",
"\n",
"def get_questions(context):\n",
" try:\n",
" response = openai.Completion.create(\n",
" engine=\"davinci-instruct-beta-v3\",\n",
" prompt=f\"Write questions based on the text below\\n\\nText: {context}\\n\\nQuestions:\\n1.\",\n",
" temperature=0,\n",
" max_tokens=257,\n",
" top_p=1,\n",
" frequency_penalty=0,\n",
" presence_penalty=0,\n",
" stop=[\"\\n\\n\"]\n",
" )\n",
" return response['choices'][0]['text']\n",
" response = client.chat.completions.create(model=\"davinci-instruct-beta-v3\",\n",
" prompt=f\"Write questions based on the text below\\n\\nText: {context}\\n\\nQuestions:\\n1.\",\n",
" temperature=0,\n",
" max_tokens=257,\n",
" top_p=1,\n",
" frequency_penalty=0,\n",
" presence_penalty=0,\n",
" stop=[\"\\n\\n\"])\n",
" return response.choices[0].text\n",
" except:\n",
" return \"\"\n",
"\n",
@ -254,7 +254,7 @@
"source": [
"def get_answers(row):\n",
" try:\n",
" response = openai.Completion.create(\n",
" response = client.chat.completions.create(\n",
" engine=\"davinci-instruct-beta-v3\",\n",
" prompt=f\"Write answer based on the text below\\n\\nText: {row.context}\\n\\nQuestions:\\n{row.questions}\\n\\nAnswers:\\n1.\",\n",
" temperature=0,\n",
@ -263,7 +263,7 @@
" frequency_penalty=0,\n",
" presence_penalty=0\n",
" )\n",
" return response['choices'][0]['text']\n",
" return response.choices[0].text\n",
" except Exception as e:\n",
" print (e)\n",
" return \"\"\n",
@ -320,7 +320,7 @@
"df = df[df.tokens<2000]\n",
"df[['context', 'tokens']].rename(columns={'context':'text','tokens':'metadata'}).to_json('olympics-data/olympics_search.jsonl', orient='records', lines=True)\n",
"\n",
"search_file = openai.File.create(\n",
"search_file = client.files.create(\n",
" file=open(\"olympics-data/olympics_search.jsonl\"),\n",
" purpose='search'\n",
")\n",
@ -473,6 +473,7 @@
" \"\"\"\n",
" \n",
" try:\n",
" # TODO: openai.Engine(search_model) is deprecated\n",
" results = openai.Engine(search_model).search(\n",
" search_model=search_model, \n",
" query=question, \n",

@ -226,7 +226,7 @@
" - one originating from the same wikipedia article\n",
" - another, which is most similar to the correct context\n",
"\n",
"This process is noisy, as sometimes the question might be answerable given a different context, but on average we hope this won't affect the peformance too much.\n",
"This process is noisy, as sometimes the question might be answerable given a different context, but on average we hope this won't affect the performance too much.\n",
"\n",
"We apply the same process of dataset creation for both the discriminator, and the Q&A answering model. We apply the process separately for the training and testing set, to ensure that the examples from the training set don't feature within the test set."
]
@ -244,6 +244,7 @@
" Find similar contexts to the given context using the search file\n",
" \"\"\"\n",
" try:\n",
" # TODO: openai.Engine(search_model) is deprecated\n",
" results = openai.Engine(search_model).search(\n",
" search_model=search_model, \n",
" query=question, \n",
@ -438,7 +439,7 @@
" Apply the fine tuned discriminator to a question, to assess whether it can be answered from the context.\n",
" \"\"\"\n",
" prompt = f\"{context}\\nQuestion: {question}\\n Related:\"\n",
" result = openai.Completion.create(model=discriminator_model, prompt=prompt, max_tokens=1, temperature=0, top_p=1, n=1, logprobs=2)\n",
" result = openai.chat.completions.create(model=discriminator_model, prompt=prompt, max_tokens=1, temperature=0, top_p=1, n=1, logprobs=2)\n",
" return result['choices'][0]['logprobs']['top_logprobs']\n",
"\n",
"apply_ft_discriminator('The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957.', \n",
@ -474,7 +475,7 @@
" Apply the fine tuned discriminator to a question\n",
" \"\"\"\n",
" prompt = f\"{context}\\nQuestion: {question}\\nAnswer:\"\n",
" result = openai.Completion.create(model=answering_model, prompt=prompt, max_tokens=30, temperature=0, top_p=1, n=1, stop=['.','\\n'])\n",
" result = openai.chat.completions.create(model=answering_model, prompt=prompt, max_tokens=30, temperature=0, top_p=1, n=1, stop=['.','\\n'])\n",
" return result['choices'][0]['text']\n",
"\n",
"apply_ft_qa_answer('The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957.', \n",

@ -41,12 +41,23 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 1,
"id": "9617e95e",
"metadata": {},
"outputs": [],
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import openai\n",
"from openai import OpenAI\n",
"import json\n",
"import boto3\n",
"import os\n",
@ -69,18 +80,18 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 2,
"id": "6d5b1991",
"metadata": {},
"outputs": [],
"source": [
"openai.api_key = os.environ.get(\"OPENAI_API_KEY\")\n",
"OpenAI.api_key = os.environ.get(\"OPENAI_API_KEY\")\n",
"GPT_MODEL = \"gpt-3.5-turbo\""
]
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 3,
"id": "a571b8d7",
"metadata": {},
"outputs": [],
@ -90,7 +101,10 @@
"# os.environ['AWS_SECRET_ACCESS_KEY'] = ''\n",
"\n",
"# Create S3 client\n",
"s3_client = boto3.client('s3')"
"s3_client = boto3.client('s3')\n",
"\n",
"# Create openai client\n",
"client = OpenAI()"
]
},
{
@ -113,73 +127,88 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 4,
"id": "da4a804b",
"metadata": {},
"outputs": [],
"source": [
"# Functions dict to pass S3 operations details for the GPT model\n",
"functions = [\n",
" {\n",
" \"name\": \"list_buckets\",\n",
" \"description\": \"List all available S3 buckets\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {}\n",
" { \n",
" \"type\": \"function\",\n",
" \"function\":{\n",
" \"name\": \"list_buckets\",\n",
" \"description\": \"List all available S3 buckets\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {}\n",
" }\n",
" }\n",
" },\n",
" {\n",
" \"name\": \"list_objects\",\n",
" \"description\": \"List the objects or files inside a given S3 bucket\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"bucket\": {\"type\": \"string\", \"description\": \"The name of the S3 bucket\"},\n",
" \"prefix\": {\"type\": \"string\", \"description\": \"The folder path in the S3 bucket\"},\n",
" \"type\": \"function\",\n",
" \"function\":{\n",
" \"name\": \"list_objects\",\n",
" \"description\": \"List the objects or files inside a given S3 bucket\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"bucket\": {\"type\": \"string\", \"description\": \"The name of the S3 bucket\"},\n",
" \"prefix\": {\"type\": \"string\", \"description\": \"The folder path in the S3 bucket\"},\n",
" },\n",
" \"required\": [\"bucket\"],\n",
" },\n",
" \"required\": [\"bucket\"],\n",
" },\n",
" }\n",
" },\n",
" {\n",
" \"name\": \"download_file\",\n",
" \"description\": \"Download a specific file from an S3 bucket to a local distribution folder.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"bucket\": {\"type\": \"string\", \"description\": \"The name of the S3 bucket\"},\n",
" \"key\": {\"type\": \"string\", \"description\": \"The path to the file inside the bucket\"},\n",
" \"directory\": {\"type\": \"string\", \"description\": \"The local destination directory to download the file, should be specificed by the user.\"},\n",
" },\n",
" \"required\": [\"bucket\", \"key\", \"directory\"],\n",
" { \n",
" \"type\": \"function\",\n",
" \"function\":{\n",
" \"name\": \"download_file\",\n",
" \"description\": \"Download a specific file from an S3 bucket to a local distribution folder.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"bucket\": {\"type\": \"string\", \"description\": \"The name of the S3 bucket\"},\n",
" \"key\": {\"type\": \"string\", \"description\": \"The path to the file inside the bucket\"},\n",
" \"directory\": {\"type\": \"string\", \"description\": \"The local destination directory to download the file, should be specificed by the user.\"},\n",
" },\n",
" \"required\": [\"bucket\", \"key\", \"directory\"],\n",
" }\n",
" }\n",
" },\n",
" {\n",
" \"name\": \"upload_file\",\n",
" \"description\": \"Upload a file to an S3 bucket\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"source\": {\"type\": \"string\", \"description\": \"The local source path or remote URL\"},\n",
" \"bucket\": {\"type\": \"string\", \"description\": \"The name of the S3 bucket\"},\n",
" \"key\": {\"type\": \"string\", \"description\": \"The path to the file inside the bucket\"},\n",
" \"is_remote_url\": {\"type\": \"boolean\", \"description\": \"Is the provided source a URL (True) or local path (False)\"},\n",
" },\n",
" \"required\": [\"source\", \"bucket\", \"key\", \"is_remote_url\"],\n",
" \"type\": \"function\",\n",
" \"function\":{\n",
" \"name\": \"upload_file\",\n",
" \"description\": \"Upload a file to an S3 bucket\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"source\": {\"type\": \"string\", \"description\": \"The local source path or remote URL\"},\n",
" \"bucket\": {\"type\": \"string\", \"description\": \"The name of the S3 bucket\"},\n",
" \"key\": {\"type\": \"string\", \"description\": \"The path to the file inside the bucket\"},\n",
" \"is_remote_url\": {\"type\": \"boolean\", \"description\": \"Is the provided source a URL (True) or local path (False)\"},\n",
" },\n",
" \"required\": [\"source\", \"bucket\", \"key\", \"is_remote_url\"],\n",
" }\n",
" }\n",
" },\n",
" {\n",
" \"name\": \"search_s3_objects\",\n",
" \"description\": \"Search for a specific file name inside an S3 bucket\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"search_name\": {\"type\": \"string\", \"description\": \"The name of the file you want to search for\"},\n",
" \"bucket\": {\"type\": \"string\", \"description\": \"The name of the S3 bucket\"},\n",
" \"prefix\": {\"type\": \"string\", \"description\": \"The folder path in the S3 bucket\"},\n",
" \"exact_match\": {\"type\": \"boolean\", \"description\": \"Set exact_match to True if the search should match the exact file name. Set exact_match to False to compare part of the file name string (the file contains)\"}\n",
" \"type\": \"function\",\n",
" \"function\":{\n",
" \"name\": \"search_s3_objects\",\n",
" \"description\": \"Search for a specific file name inside an S3 bucket\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"search_name\": {\"type\": \"string\", \"description\": \"The name of the file you want to search for\"},\n",
" \"bucket\": {\"type\": \"string\", \"description\": \"The name of the S3 bucket\"},\n",
" \"prefix\": {\"type\": \"string\", \"description\": \"The folder path in the S3 bucket\"},\n",
" \"exact_match\": {\"type\": \"boolean\", \"description\": \"Set exact_match to True if the search should match the exact file name. Set exact_match to False to compare part of the file name string (the file contains)\"}\n",
" },\n",
" \"required\": [\"search_name\"],\n",
" },\n",
" \"required\": [\"search_name\"],\n",
" },\n",
" }\n",
" }\n",
"]"
]
@ -195,7 +224,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 5,
"id": "cf30f14e",
"metadata": {},
"outputs": [],
@ -208,7 +237,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 6,
"id": "37736b74",
"metadata": {},
"outputs": [],
@ -275,7 +304,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 7,
"id": "03c3d555",
"metadata": {},
"outputs": [],
@ -300,7 +329,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 16,
"id": "796acfdd",
"metadata": {},
"outputs": [],
@ -309,13 +338,13 @@
" model_name=GPT_MODEL):\n",
" \n",
" if functions is not None:\n",
" return openai.ChatCompletion.create(\n",
" return client.chat.completions.create(\n",
" model=model_name,\n",
" messages=messages,\n",
" functions=functions,\n",
" function_call=function_call)\n",
" tools=functions,\n",
" tool_choice=function_call)\n",
" else:\n",
" return openai.ChatCompletion.create(\n",
" return client.chat.completions.create(\n",
" model=model_name,\n",
" messages=messages)"
]
@ -340,7 +369,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 17,
"id": "3e2e9192",
"metadata": {},
"outputs": [],
@ -354,15 +383,15 @@
" \n",
" # Call the model to get a response\n",
" response = chat_completion_request(messages, functions=functions)\n",
" response_message = response['choices'][0]['message']\n",
" response_message = response.choices[0].message\n",
" \n",
" if is_log:\n",
" print(response['choices'])\n",
" print(response.choices)\n",
" \n",
" # check if GPT wanted to call a function\n",
" if response_message.get(\"function_call\"):\n",
" function_name = response_message['function_call']['name']\n",
" function_args = json.loads(response_message['function_call']['arguments'])\n",
" if response_message.tool_calls:\n",
" function_name = response_message.tool_calls[0].function.name\n",
" function_args = json.loads(response_message.tool_calls[0].function.arguments)\n",
" \n",
" # Call the function\n",
" function_response = available_functions[function_name](**function_args)\n",
@ -370,16 +399,16 @@
" # Add the response to the conversation\n",
" messages.append(response_message)\n",
" messages.append({\n",
" \"role\": \"function\",\n",
" \"name\": function_name,\n",
" \"role\": \"tool\",\n",
" \"content\": function_response,\n",
" \"tool_call_id\": response_message.tool_calls[0].id,\n",
" })\n",
" \n",
" # Call the model again to summarize the results\n",
" second_response = chat_completion_request(messages)\n",
" final_message = second_response['choices'][0]['message']['content']\n",
" final_message = second_response.choices[0].message.content\n",
" else:\n",
" final_message = response_message['content']\n",
" final_message = response_message.content\n",
"\n",
" return final_message"
]

@ -119,7 +119,7 @@
},
"outputs": [],
"source": [
"WB_ENTITY = # set to your wandb username or team name\n",
"WB_ENTITY = \"\" # set to your wandb username or team name\n",
"WB_PROJECT = \"weave\" # top-level directory for this work\n",
"STREAM_NAME = \"openai_logs\" # record table which stores the logs of OpenAI API calls as they stream in"
]

@ -134,7 +134,7 @@
},
"source": [
"Before we start, we can configure the LLM provider and model that will power our RAG system. \n",
"Here, we pick *text-davinci-003* from OpenAI, allow unlimited output tokens. "
"Here, we pick `gpt-3.5-turbo-instruct` from OpenAI. "
]
},
{
@ -146,7 +146,7 @@
},
"outputs": [],
"source": [
"llm = OpenAI(temperature=0, model_name=\"text-davinci-003\", max_tokens=-1)"
"llm = OpenAI(temperature=0, model_name=\"gpt-3.5-turbo-instruct\", max_tokens=-1)"
]
},
{

@ -7,60 +7,57 @@ from scipy import spatial
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.metrics import average_precision_score, precision_recall_curve
from tenacity import retry, stop_after_attempt, wait_random_exponential
import openai
from openai import OpenAI
import numpy as np
import pandas as pd
client = OpenAI(max_retries=5)
@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))
def get_embedding(text: str, model="text-similarity-davinci-001", **kwargs) -> List[float]:
def get_embedding(text: str, model="text-embedding-3-small", **kwargs) -> List[float]:
# replace newlines, which can negatively affect performance.
text = text.replace("\n", " ")
response = openai.embeddings.create(input=[text], model=model, **kwargs)
response = client.embeddings.create(input=[text], model=model, **kwargs)
return response.data[0].embedding
@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))
async def aget_embedding(
text: str, model="text-similarity-davinci-001", **kwargs
text: str, model="text-embedding-3-small", **kwargs
) -> List[float]:
# replace newlines, which can negatively affect performance.
text = text.replace("\n", " ")
return (await openai.embeddings.create(input=[text], model=model, **kwargs))["data"][0][
"embedding"
]
return (await client.embeddings.create(input=[text], model=model, **kwargs))[
"data"
][0]["embedding"]
@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))
def get_embeddings(
list_of_text: List[str], model="text-similarity-babbage-001", **kwargs
list_of_text: List[str], model="text-embedding-3-small", **kwargs
) -> List[List[float]]:
assert len(list_of_text) <= 2048, "The batch size should not be larger than 2048."
# replace newlines, which can negatively affect performance.
list_of_text = [text.replace("\n", " ") for text in list_of_text]
data = openai.embeddings.create(input=list_of_text, model=model, **kwargs).data
data = client.embeddings.create(input=list_of_text, model=model, **kwargs).data
return [d.embedding for d in data]
@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))
async def aget_embeddings(
list_of_text: List[str], model="text-similarity-babbage-001", **kwargs
list_of_text: List[str], model="text-embedding-3-small", **kwargs
) -> List[List[float]]:
assert len(list_of_text) <= 2048, "The batch size should not be larger than 2048."
# replace newlines, which can negatively affect performance.
list_of_text = [text.replace("\n", " ") for text in list_of_text]
data = (await openai.embeddings.create(input=list_of_text, model=model, **kwargs)).data
data = (
await client.embeddings.create(input=list_of_text, model=model, **kwargs)
).data
return [d.embedding for d in data]

@ -371,7 +371,7 @@
"source": [
"## Search data\n",
"\n",
"Once the data is put into Qdrant we will start querying the collection for the closest vectors. We may provide an additional parameter `vector_name` to switch from title to content based search. Since the precomputed embeddings were created with `text-embedding-ada-002` OpenAI model we also have to use it during search."
"Once the data is put into Qdrant we will start querying the collection for the closest vectors. We may provide an additional parameter `vector_name` to switch from title to content based search. Since the precomputed embeddings were created with `text-embedding-3-small` OpenAI model we also have to use it during search."
]
},
{
@ -385,7 +385,7 @@
" # Creates embedding vector from user query\n",
" embedded_query = openai.Embedding.create(\n",
" input=query,\n",
" model=\"text-embedding-ada-002\",\n",
" model=\"text-embedding-3-small\",\n",
" )[\"data\"][0][\"embedding\"]\n",
"\n",
" # Convert the embedded_query to PostgreSQL compatible format\n",

@ -9,7 +9,7 @@ Each provider has their own named directory, with a standard notebook to introdu
## Guides & deep dives
- [AnalyticDB](https://www.alibabacloud.com/help/en/analyticdb-for-postgresql/latest/get-started-with-analyticdb-for-postgresql)
- [Cassandra/Astra DB](https://docs.datastax.com/en/astra-serverless/docs/vector-search/qandasimsearch-quickstart.html)
- [AzureSearch](https://learn.microsoft.com/azure/search/search-get-started-vector)
- [Azure AI Search](https://learn.microsoft.com/azure/search/search-get-started-vector)
- [Chroma](https://docs.trychroma.com/getting-started)
- [Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html)
- [Hologres](https://www.alibabacloud.com/help/en/hologres/latest/procedure-to-use-hologres)
@ -24,6 +24,7 @@ Each provider has their own named directory, with a standard notebook to introdu
- [Redis](https://github.com/RedisVentures/simple-vecsim-intro)
- [SingleStoreDB](https://www.singlestore.com/blog/how-to-get-started-with-singlestore/)
- [Supabase](https://supabase.com/docs/guides/ai)
- [Tembo](https://tembo.io/docs/tembo-stacks/vector-db)
- [Typesense](https://typesense.org/docs/guide/)
- [Weaviate](https://weaviate.io/developers/weaviate/quickstart)
- [Zilliz](https://docs.zilliz.com/docs/quick-start-1)

@ -46,8 +46,7 @@
"source": [
"import openai\n",
"\n",
"# models\n",
"EMBEDDING_MODEL = \"text-embedding-ada-002\"\n",
"EMBEDDING_MODEL = \"text-embedding-3-small\"\n",
"GPT_MODEL = \"gpt-3.5-turbo\"\n"
]
},

@ -429,7 +429,7 @@
"source": [
"## Search data\n",
"\n",
"Once the data is put into Qdrant we will start querying the collection for the closest vectors. We may provide an additional parameter `vector_name` to switch from title to content based search. Since the precomputed embeddings were created with `text-embedding-ada-002` OpenAI model we also have to use it during search.\n"
"Once the data is put into Qdrant we will start querying the collection for the closest vectors. We may provide an additional parameter `vector_name` to switch from title to content based search. Since the precomputed embeddings were created with `text-embedding-3-small` OpenAI model we also have to use it during search.\n"
]
},
{
@ -448,7 +448,7 @@
" # Creates embedding vector from user query\n",
" embedded_query = openai.Embedding.create(\n",
" input=query,\n",
" model=\"text-embedding-ada-002\",\n",
" model=\"text-embedding-3-small\",\n",
" )[\"data\"][0][\"embedding\"]\n",
"\n",
" # Convert the embedded_query to PostgreSQL compatible format\n",

@ -0,0 +1,738 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Azure AI Search as a vector database for OpenAI embeddings"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook provides step by step instuctions on using Azure AI Search (f.k.a Azure Cognitive Search) as a vector database with OpenAI embeddings. Azure AI Search is a cloud search service that gives developers infrastructure, APIs, and tools for building a rich search experience over private, heterogeneous content in web, mobile, and enterprise applications.\n",
"\n",
"## Prerequistites:\n",
"For the purposes of this exercise you must have the following:\n",
"- [Azure AI Search Service](https://learn.microsoft.com/azure/search/)\n",
"- [OpenAI Key](https://platform.openai.com/account/api-keys) or [Azure OpenAI credentials](https://learn.microsoft.com/azure/cognitive-services/openai/)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"! pip install wget\n",
"! pip install azure-search-documents \n",
"! pip install azure-identity\n",
"! pip install openai"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Import required libraries"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import json \n",
"import wget\n",
"import pandas as pd\n",
"import zipfile\n",
"from openai import AzureOpenAI\n",
"from azure.identity import DefaultAzureCredential, get_bearer_token_provider\n",
"from azure.core.credentials import AzureKeyCredential \n",
"from azure.search.documents import SearchClient, SearchIndexingBufferedSender \n",
"from azure.search.documents.indexes import SearchIndexClient \n",
"from azure.search.documents.models import (\n",
" QueryAnswerType,\n",
" QueryCaptionType,\n",
" QueryType,\n",
" VectorizedQuery,\n",
")\n",
"from azure.search.documents.indexes.models import (\n",
" HnswAlgorithmConfiguration,\n",
" HnswParameters,\n",
" SearchField,\n",
" SearchableField,\n",
" SearchFieldDataType,\n",
" SearchIndex,\n",
" SemanticConfiguration,\n",
" SemanticField,\n",
" SemanticPrioritizedFields,\n",
" SemanticSearch,\n",
" SimpleField,\n",
" VectorSearch,\n",
" VectorSearchAlgorithmKind,\n",
" VectorSearchAlgorithmMetric,\n",
" VectorSearchProfile,\n",
")\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Configure OpenAI settings\n",
"\n",
"This section guides you through setting up authentication for Azure OpenAI, allowing you to securely interact with the service using either Azure Active Directory (AAD) or an API key. Before proceeding, ensure you have your Azure OpenAI endpoint and credentials ready. For detailed instructions on setting up AAD with Azure OpenAI, refer to the [official documentation](https://learn.microsoft.com/azure/ai-services/openai/how-to/managed-identity).\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"endpoint: str = \"YOUR_AZURE_OPENAI_ENDPOINT\"\n",
"api_key: str = \"YOUR_AZURE_OPENAI_KEY\"\n",
"api_version: str = \"2023-05-15\"\n",
"deployment = \"YOUR_AZURE_OPENAI_DEPLOYMENT_NAME\"\n",
"credential = DefaultAzureCredential()\n",
"token_provider = get_bearer_token_provider(\n",
" credential, \"https://cognitiveservices.azure.com/.default\"\n",
")\n",
"\n",
"# Set this flag to True if you are using Azure Active Directory\n",
"use_aad_for_aoai = True \n",
"\n",
"if use_aad_for_aoai:\n",
" # Use Azure Active Directory (AAD) authentication\n",
" client = AzureOpenAI(\n",
" azure_endpoint=endpoint,\n",
" api_version=api_version,\n",
" azure_ad_token_provider=token_provider,\n",
" )\n",
"else:\n",
" # Use API key authentication\n",
" client = AzureOpenAI(\n",
" api_key=api_key,\n",
" api_version=api_version,\n",
" azure_endpoint=endpoint,\n",
" )"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Configure Azure AI Search Vector Store settings\n",
"This section explains how to set up the Azure AI Search client for integrating with the Vector Store feature. You can locate your Azure AI Search service details in the Azure Portal or programmatically via the [Search Management SDK](https://learn.microsoft.com/rest/api/searchmanagement/).\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"# Configuration\n",
"search_service_endpoint: str = \"YOUR_AZURE_SEARCH_ENDPOINT\"\n",
"search_service_api_key: str = \"YOUR_AZURE_SEARCH_ADMIN_KEY\"\n",
"index_name: str = \"azure-ai-search-openai-cookbook-demo\"\n",
"\n",
"# Set this flag to True if you are using Azure Active Directory\n",
"use_aad_for_search = True \n",
"\n",
"if use_aad_for_search:\n",
" # Use Azure Active Directory (AAD) authentication\n",
" credential = DefaultAzureCredential()\n",
"else:\n",
" # Use API key authentication\n",
" credential = AzureKeyCredential(search_service_api_key)\n",
"\n",
"# Initialize the SearchClient with the selected authentication method\n",
"search_client = SearchClient(\n",
" endpoint=search_service_endpoint, index_name=index_name, credential=credential\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Load data\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'vector_database_wikipedia_articles_embedded.zip'"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"embeddings_url = \"https://cdn.openai.com/API/examples/data/vector_database_wikipedia_articles_embedded.zip\"\n",
"\n",
"# The file is ~700 MB so this will take some time\n",
"wget.download(embeddings_url)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"with zipfile.ZipFile(\"vector_database_wikipedia_articles_embedded.zip\", \"r\") as zip_ref:\n",
" zip_ref.extractall(\"../../data\")"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>id</th>\n",
" <th>url</th>\n",
" <th>title</th>\n",
" <th>text</th>\n",
" <th>title_vector</th>\n",
" <th>content_vector</th>\n",
" <th>vector_id</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>https://simple.wikipedia.org/wiki/April</td>\n",
" <td>April</td>\n",
" <td>April is the fourth month of the year in the J...</td>\n",
" <td>[0.001009464613161981, -0.020700545981526375, ...</td>\n",
" <td>[-0.011253940872848034, -0.013491976074874401,...</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>https://simple.wikipedia.org/wiki/August</td>\n",
" <td>August</td>\n",
" <td>August (Aug.) is the eighth month of the year ...</td>\n",
" <td>[0.0009286514250561595, 0.000820168002974242, ...</td>\n",
" <td>[0.0003609954728744924, 0.007262262050062418, ...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>6</td>\n",
" <td>https://simple.wikipedia.org/wiki/Art</td>\n",
" <td>Art</td>\n",
" <td>Art is a creative activity that expresses imag...</td>\n",
" <td>[0.003393713850528002, 0.0061537534929811954, ...</td>\n",
" <td>[-0.004959689453244209, 0.015772193670272827, ...</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>8</td>\n",
" <td>https://simple.wikipedia.org/wiki/A</td>\n",
" <td>A</td>\n",
" <td>A or a is the first letter of the English alph...</td>\n",
" <td>[0.0153952119871974, -0.013759135268628597, 0....</td>\n",
" <td>[0.024894846603274345, -0.022186409682035446, ...</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>9</td>\n",
" <td>https://simple.wikipedia.org/wiki/Air</td>\n",
" <td>Air</td>\n",
" <td>Air refers to the Earth's atmosphere. Air is a...</td>\n",
" <td>[0.02224554680287838, -0.02044147066771984, -0...</td>\n",
" <td>[0.021524671465158463, 0.018522677943110466, -...</td>\n",
" <td>4</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" id url title \\\n",
"0 1 https://simple.wikipedia.org/wiki/April April \n",
"1 2 https://simple.wikipedia.org/wiki/August August \n",
"2 6 https://simple.wikipedia.org/wiki/Art Art \n",
"3 8 https://simple.wikipedia.org/wiki/A A \n",
"4 9 https://simple.wikipedia.org/wiki/Air Air \n",
"\n",
" text \\\n",
"0 April is the fourth month of the year in the J... \n",
"1 August (Aug.) is the eighth month of the year ... \n",
"2 Art is a creative activity that expresses imag... \n",
"3 A or a is the first letter of the English alph... \n",
"4 Air refers to the Earth's atmosphere. Air is a... \n",
"\n",
" title_vector \\\n",
"0 [0.001009464613161981, -0.020700545981526375, ... \n",
"1 [0.0009286514250561595, 0.000820168002974242, ... \n",
"2 [0.003393713850528002, 0.0061537534929811954, ... \n",
"3 [0.0153952119871974, -0.013759135268628597, 0.... \n",
"4 [0.02224554680287838, -0.02044147066771984, -0... \n",
"\n",
" content_vector vector_id \n",
"0 [-0.011253940872848034, -0.013491976074874401,... 0 \n",
"1 [0.0003609954728744924, 0.007262262050062418, ... 1 \n",
"2 [-0.004959689453244209, 0.015772193670272827, ... 2 \n",
"3 [0.024894846603274345, -0.022186409682035446, ... 3 \n",
"4 [0.021524671465158463, 0.018522677943110466, -... 4 "
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"article_df = pd.read_csv(\"../../data/vector_database_wikipedia_articles_embedded.csv\")\n",
"\n",
"# Read vectors from strings back into a list using json.loads\n",
"article_df[\"title_vector\"] = article_df.title_vector.apply(json.loads)\n",
"article_df[\"content_vector\"] = article_df.content_vector.apply(json.loads)\n",
"article_df[\"vector_id\"] = article_df[\"vector_id\"].apply(str)\n",
"article_df.head()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Create an index\n",
"This code snippet demonstrates how to define and create a search index using the `SearchIndexClient` from the Azure AI Search Python SDK. The index incorporates both vector search and semantic ranker capabilities. For more details, visit our documentation on how to [Create a Vector Index](https://learn.microsoft.com/azure/search/vector-search-how-to-create-index?.tabs=config-2023-11-01%2Crest-2023-11-01%2Cpush%2Cportal-check-index)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"azure-ai-search-openai-cookbook-demo created\n"
]
}
],
"source": [
"# Initialize the SearchIndexClient\n",
"index_client = SearchIndexClient(\n",
" endpoint=search_service_endpoint, credential=credential\n",
")\n",
"\n",
"# Define the fields for the index\n",
"fields = [\n",
" SimpleField(name=\"id\", type=SearchFieldDataType.String),\n",
" SimpleField(name=\"vector_id\", type=SearchFieldDataType.String, key=True),\n",
" SimpleField(name=\"url\", type=SearchFieldDataType.String),\n",
" SearchableField(name=\"title\", type=SearchFieldDataType.String),\n",
" SearchableField(name=\"text\", type=SearchFieldDataType.String),\n",
" SearchField(\n",
" name=\"title_vector\",\n",
" type=SearchFieldDataType.Collection(SearchFieldDataType.Single),\n",
" vector_search_dimensions=1536,\n",
" vector_search_profile_name=\"my-vector-config\",\n",
" ),\n",
" SearchField(\n",
" name=\"content_vector\",\n",
" type=SearchFieldDataType.Collection(SearchFieldDataType.Single),\n",
" vector_search_dimensions=1536,\n",
" vector_search_profile_name=\"my-vector-config\",\n",
" ),\n",
"]\n",
"\n",
"# Configure the vector search configuration\n",
"vector_search = VectorSearch(\n",
" algorithms=[\n",
" HnswAlgorithmConfiguration(\n",
" name=\"my-hnsw\",\n",
" kind=VectorSearchAlgorithmKind.HNSW,\n",
" parameters=HnswParameters(\n",
" m=4,\n",
" ef_construction=400,\n",
" ef_search=500,\n",
" metric=VectorSearchAlgorithmMetric.COSINE,\n",
" ),\n",
" )\n",
" ],\n",
" profiles=[\n",
" VectorSearchProfile(\n",
" name=\"my-vector-config\",\n",
" algorithm_configuration_name=\"my-hnsw\",\n",
" )\n",
" ],\n",
")\n",
"\n",
"# Configure the semantic search configuration\n",
"semantic_search = SemanticSearch(\n",
" configurations=[\n",
" SemanticConfiguration(\n",
" name=\"my-semantic-config\",\n",
" prioritized_fields=SemanticPrioritizedFields(\n",
" title_field=SemanticField(field_name=\"title\"),\n",
" keywords_fields=[SemanticField(field_name=\"url\")],\n",
" content_fields=[SemanticField(field_name=\"text\")],\n",
" ),\n",
" )\n",
" ]\n",
")\n",
"\n",
"# Create the search index with the vector search and semantic search configurations\n",
"index = SearchIndex(\n",
" name=index_name,\n",
" fields=fields,\n",
" vector_search=vector_search,\n",
" semantic_search=semantic_search,\n",
")\n",
"\n",
"# Create or update the index\n",
"result = index_client.create_or_update_index(index)\n",
"print(f\"{result.name} created\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uploading Data to Azure AI Search Index\n",
"\n",
"The following code snippet outlines the process of uploading a batch of documents—specifically, Wikipedia articles with pre-computed embeddings—from a pandas DataFrame to an Azure AI Search index. For a detailed guide on data import strategies and best practices, refer to [Data Import in Azure AI Search](https://learn.microsoft.com/azure/search/search-what-is-data-import).\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Uploaded 25000 documents in total\n"
]
}
],
"source": [
"from azure.core.exceptions import HttpResponseError\n",
"\n",
"# Convert the 'id' and 'vector_id' columns to string so one of them can serve as our key field\n",
"article_df[\"id\"] = article_df[\"id\"].astype(str)\n",
"article_df[\"vector_id\"] = article_df[\"vector_id\"].astype(str)\n",
"# Convert the DataFrame to a list of dictionaries\n",
"documents = article_df.to_dict(orient=\"records\")\n",
"\n",
"# Create a SearchIndexingBufferedSender\n",
"batch_client = SearchIndexingBufferedSender(\n",
" search_service_endpoint, index_name, credential\n",
")\n",
"\n",
"try:\n",
" # Add upload actions for all documents in a single call\n",
" batch_client.upload_documents(documents=documents)\n",
"\n",
" # Manually flush to send any remaining documents in the buffer\n",
" batch_client.flush()\n",
"except HttpResponseError as e:\n",
" print(f\"An error occurred: {e}\")\n",
"finally:\n",
" # Clean up resources\n",
" batch_client.close()\n",
"\n",
"print(f\"Uploaded {len(documents)} documents in total\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If your dataset didn't already contain pre-computed embeddings, you can create embeddings by using the below function using the `openai` python library. You'll also notice the same function and model are being used to generate query embeddings for performing vector searches."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Content: April is the fourth month of the year in the Julian and Gregorian calendars, and comes between March\n",
"Content vector generated\n"
]
}
],
"source": [
"# Example function to generate document embedding\n",
"def generate_embeddings(text, model):\n",
" # Generate embeddings for the provided text using the specified model\n",
" embeddings_response = client.embeddings.create(model=model, input=text)\n",
" # Extract the embedding data from the response\n",
" embedding = embeddings_response.data[0].embedding\n",
" return embedding\n",
"\n",
"\n",
"first_document_content = documents[0][\"text\"]\n",
"print(f\"Content: {first_document_content[:100]}\")\n",
"\n",
"content_vector = generate_embeddings(first_document_content, deployment)\n",
"print(\"Content vector generated\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Perform a vector similarity search"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Title: Documenta\n",
"Score: 0.8599451\n",
"URL: https://simple.wikipedia.org/wiki/Documenta\n",
"\n",
"Title: Museum of Modern Art\n",
"Score: 0.85260946\n",
"URL: https://simple.wikipedia.org/wiki/Museum%20of%20Modern%20Art\n",
"\n",
"Title: Expressionism\n",
"Score: 0.852354\n",
"URL: https://simple.wikipedia.org/wiki/Expressionism\n",
"\n"
]
}
],
"source": [
"# Pure Vector Search\n",
"query = \"modern art in Europe\"\n",
" \n",
"search_client = SearchClient(search_service_endpoint, index_name, credential) \n",
"vector_query = VectorizedQuery(vector=generate_embeddings(query, deployment), k_nearest_neighbors=3, fields=\"content_vector\")\n",
" \n",
"results = search_client.search( \n",
" search_text=None, \n",
" vector_queries= [vector_query], \n",
" select=[\"title\", \"text\", \"url\"] \n",
")\n",
" \n",
"for result in results: \n",
" print(f\"Title: {result['title']}\") \n",
" print(f\"Score: {result['@search.score']}\") \n",
" print(f\"URL: {result['url']}\\n\") "
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Perform a Hybrid Search\n",
"Hybrid search combines the capabilities of traditional keyword-based search with vector-based similarity search to provide more relevant and contextual results. This approach is particularly useful when dealing with complex queries that benefit from understanding the semantic meaning behind the text.\n",
"\n",
"The provided code snippet demonstrates how to execute a hybrid search query:"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Title: Wars of Scottish Independence\n",
"Score: 0.03306011110544205\n",
"URL: https://simple.wikipedia.org/wiki/Wars%20of%20Scottish%20Independence\n",
"\n",
"Title: Battle of Bannockburn\n",
"Score: 0.022253260016441345\n",
"URL: https://simple.wikipedia.org/wiki/Battle%20of%20Bannockburn\n",
"\n",
"Title: Scottish\n",
"Score: 0.016393441706895828\n",
"URL: https://simple.wikipedia.org/wiki/Scottish\n",
"\n"
]
}
],
"source": [
"# Hybrid Search\n",
"query = \"Famous battles in Scottish history\" \n",
" \n",
"search_client = SearchClient(search_service_endpoint, index_name, credential) \n",
"vector_query = VectorizedQuery(vector=generate_embeddings(query, deployment), k_nearest_neighbors=3, fields=\"content_vector\")\n",
" \n",
"results = search_client.search( \n",
" search_text=query, \n",
" vector_queries= [vector_query], \n",
" select=[\"title\", \"text\", \"url\"],\n",
" top=3\n",
")\n",
" \n",
"for result in results: \n",
" print(f\"Title: {result['title']}\") \n",
" print(f\"Score: {result['@search.score']}\") \n",
" print(f\"URL: {result['url']}\\n\") "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Perform a Hybrid Search with Reranking (powered by Bing)\n",
"[Semantic ranker](https://learn.microsoft.com/azure/search/semantic-search-overview) measurably improves search relevance by using language understanding to rerank search results. Additionally, you can get extractive captions, answers, and highlights. "
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Semantic Answer: Advancements During the industrial revolution, new technology brought many changes. For example:<em> Canals</em> were built to allow heavy goods to be moved easily where they were needed. The steam engine became the main source of power. It replaced horses and human labor. Cheap iron and steel became mass-produced.\n",
"Semantic Answer Score: 0.90478515625\n",
"\n",
"Title: Industrial Revolution\n",
"Reranker Score: 3.408700942993164\n",
"URL: https://simple.wikipedia.org/wiki/Industrial%20Revolution\n",
"Caption: Advancements During the industrial revolution, new technology brought many changes. For example: Canals were built to allow heavy goods to be moved easily where they were needed. The steam engine became the main source of power. It replaced horses and human labor. Cheap iron and steel became mass-produced.\n",
"\n",
"Title: Printing\n",
"Reranker Score: 1.603400707244873\n",
"URL: https://simple.wikipedia.org/wiki/Printing\n",
"Caption: Machines to speed printing, cheaper paper, automatic stitching and binding all arrived in the 19th century during the industrial revolution. What had once been done by a few men by hand was now done by limited companies on huge machines. The result was much lower prices, and a much wider readership.\n",
"\n",
"Title: Industrialisation\n",
"Reranker Score: 1.3238357305526733\n",
"URL: https://simple.wikipedia.org/wiki/Industrialisation\n",
"Caption: <em>Industrialisation</em> (or<em> industrialization)</em> is a process that happens in countries when they start to use machines to do work that was once done by people.<em> Industrialisation changes</em> the things people do.<em> Industrialisation</em> caused towns to grow larger. Many people left farming to take higher paid jobs in factories in towns.\n",
"\n"
]
}
],
"source": [
"# Semantic Hybrid Search\n",
"query = \"What were the key technological advancements during the Industrial Revolution?\"\n",
"\n",
"search_client = SearchClient(search_service_endpoint, index_name, credential)\n",
"vector_query = VectorizedQuery(\n",
" vector=generate_embeddings(query, deployment),\n",
" k_nearest_neighbors=3,\n",
" fields=\"content_vector\",\n",
")\n",
"\n",
"results = search_client.search(\n",
" search_text=query,\n",
" vector_queries=[vector_query],\n",
" select=[\"title\", \"text\", \"url\"],\n",
" query_type=QueryType.SEMANTIC,\n",
" semantic_configuration_name=\"my-semantic-config\",\n",
" query_caption=QueryCaptionType.EXTRACTIVE,\n",
" query_answer=QueryAnswerType.EXTRACTIVE,\n",
" top=3,\n",
")\n",
"\n",
"semantic_answers = results.get_answers()\n",
"for answer in semantic_answers:\n",
" if answer.highlights:\n",
" print(f\"Semantic Answer: {answer.highlights}\")\n",
" else:\n",
" print(f\"Semantic Answer: {answer.text}\")\n",
" print(f\"Semantic Answer Score: {answer.score}\\n\")\n",
"\n",
"for result in results:\n",
" print(f\"Title: {result['title']}\")\n",
" print(f\"Reranker Score: {result['@search.reranker_score']}\")\n",
" print(f\"URL: {result['url']}\")\n",
" captions = result[\"@search.captions\"]\n",
" if captions:\n",
" caption = captions[0]\n",
" if caption.highlights:\n",
" print(f\"Caption: {caption.highlights}\\n\")\n",
" else:\n",
" print(f\"Caption: {caption.text}\\n\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}

@ -1,647 +0,0 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Azure Cognitive Search as a vector database for OpenAI embeddings"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook provides step by step instuctions on using Azure Cognitive Search as a vector database with OpenAI embeddings. Azure Cognitive Search (formerly known as \"Azure Search\") is a cloud search service that gives developers infrastructure, APIs, and tools for building a rich search experience over private, heterogeneous content in web, mobile, and enterprise applications.\n",
"\n",
"## Prerequistites:\n",
"For the purposes of this exercise you must have the following:\n",
"- [Azure Cognitive Search Service](https://learn.microsoft.com/azure/search/)\n",
"- [OpenAI Key](https://platform.openai.com/account/api-keys) or [Azure OpenAI credentials](https://learn.microsoft.com/azure/cognitive-services/openai/)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"! pip install wget\n",
"! pip install azure-search-documents --pre "
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Import required libraries"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import openai\n",
"import json \n",
"import openai\n",
"import wget\n",
"import pandas as pd\n",
"import zipfile\n",
"from azure.core.credentials import AzureKeyCredential \n",
"from azure.search.documents import SearchClient \n",
"from azure.search.documents.indexes import SearchIndexClient \n",
"from azure.search.documents.models import Vector \n",
"from azure.search.documents import SearchIndexingBufferedSender\n",
"from azure.search.documents.indexes.models import ( \n",
" SearchIndex, \n",
" SearchField, \n",
" SearchFieldDataType, \n",
" SimpleField, \n",
" SearchableField, \n",
" SearchIndex, \n",
" SemanticConfiguration, \n",
" PrioritizedFields, \n",
" SemanticField, \n",
" SearchField, \n",
" SemanticSettings, \n",
" VectorSearch, \n",
" HnswVectorSearchAlgorithmConfiguration, \n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Configure OpenAI settings\n",
"\n",
"Configure your OpenAI or Azure OpenAI settings. For this example, we use Azure OpenAI."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"openai.api_type = \"azure\"\n",
"openai.api_base = \"YOUR_AZURE_OPENAI_ENDPOINT\"\n",
"openai.api_version = \"2023-05-15\"\n",
"openai.api_key = \"YOUR_AZURE_OPENAI_KEY\"\n",
"model: str = \"text-embedding-ada-002\""
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Configure Azure Cognitive Search Vector Store settings\n",
"You can find this in the Azure Portal or using the [Search Management SDK](https://learn.microsoft.com/rest/api/searchmanagement/)\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"search_service_endpoint: str = \"YOUR_AZURE_SEARCH_ENDPOINT\"\n",
"search_service_api_key: str = \"YOUR_AZURE_SEARCH_ADMIN_KEY\"\n",
"index_name: str = \"azure-cognitive-search-vector-demo\"\n",
"credential = AzureKeyCredential(search_service_api_key)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Load data\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'vector_database_wikipedia_articles_embedded.zip'"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"embeddings_url = \"https://cdn.openai.com/API/examples/data/vector_database_wikipedia_articles_embedded.zip\"\n",
"\n",
"# The file is ~700 MB so this will take some time\n",
"wget.download(embeddings_url)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"with zipfile.ZipFile(\"vector_database_wikipedia_articles_embedded.zip\",\"r\") as zip_ref:\n",
" zip_ref.extractall(\"../../data\")"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>id</th>\n",
" <th>url</th>\n",
" <th>title</th>\n",
" <th>text</th>\n",
" <th>title_vector</th>\n",
" <th>content_vector</th>\n",
" <th>vector_id</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>https://simple.wikipedia.org/wiki/April</td>\n",
" <td>April</td>\n",
" <td>April is the fourth month of the year in the J...</td>\n",
" <td>[0.001009464613161981, -0.020700545981526375, ...</td>\n",
" <td>[-0.011253940872848034, -0.013491976074874401,...</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>https://simple.wikipedia.org/wiki/August</td>\n",
" <td>August</td>\n",
" <td>August (Aug.) is the eighth month of the year ...</td>\n",
" <td>[0.0009286514250561595, 0.000820168002974242, ...</td>\n",
" <td>[0.0003609954728744924, 0.007262262050062418, ...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>6</td>\n",
" <td>https://simple.wikipedia.org/wiki/Art</td>\n",
" <td>Art</td>\n",
" <td>Art is a creative activity that expresses imag...</td>\n",
" <td>[0.003393713850528002, 0.0061537534929811954, ...</td>\n",
" <td>[-0.004959689453244209, 0.015772193670272827, ...</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>8</td>\n",
" <td>https://simple.wikipedia.org/wiki/A</td>\n",
" <td>A</td>\n",
" <td>A or a is the first letter of the English alph...</td>\n",
" <td>[0.0153952119871974, -0.013759135268628597, 0....</td>\n",
" <td>[0.024894846603274345, -0.022186409682035446, ...</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>9</td>\n",
" <td>https://simple.wikipedia.org/wiki/Air</td>\n",
" <td>Air</td>\n",
" <td>Air refers to the Earth's atmosphere. Air is a...</td>\n",
" <td>[0.02224554680287838, -0.02044147066771984, -0...</td>\n",
" <td>[0.021524671465158463, 0.018522677943110466, -...</td>\n",
" <td>4</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" id url title \\\n",
"0 1 https://simple.wikipedia.org/wiki/April April \n",
"1 2 https://simple.wikipedia.org/wiki/August August \n",
"2 6 https://simple.wikipedia.org/wiki/Art Art \n",
"3 8 https://simple.wikipedia.org/wiki/A A \n",
"4 9 https://simple.wikipedia.org/wiki/Air Air \n",
"\n",
" text \\\n",
"0 April is the fourth month of the year in the J... \n",
"1 August (Aug.) is the eighth month of the year ... \n",
"2 Art is a creative activity that expresses imag... \n",
"3 A or a is the first letter of the English alph... \n",
"4 Air refers to the Earth's atmosphere. Air is a... \n",
"\n",
" title_vector \\\n",
"0 [0.001009464613161981, -0.020700545981526375, ... \n",
"1 [0.0009286514250561595, 0.000820168002974242, ... \n",
"2 [0.003393713850528002, 0.0061537534929811954, ... \n",
"3 [0.0153952119871974, -0.013759135268628597, 0.... \n",
"4 [0.02224554680287838, -0.02044147066771984, -0... \n",
"\n",
" content_vector vector_id \n",
"0 [-0.011253940872848034, -0.013491976074874401,... 0 \n",
"1 [0.0003609954728744924, 0.007262262050062418, ... 1 \n",
"2 [-0.004959689453244209, 0.015772193670272827, ... 2 \n",
"3 [0.024894846603274345, -0.022186409682035446, ... 3 \n",
"4 [0.021524671465158463, 0.018522677943110466, -... 4 "
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"article_df = pd.read_csv('../../data/vector_database_wikipedia_articles_embedded.csv') \n",
" \n",
"# Read vectors from strings back into a list using json.loads \n",
"article_df[\"title_vector\"] = article_df.title_vector.apply(json.loads) \n",
"article_df[\"content_vector\"] = article_df.content_vector.apply(json.loads) \n",
"article_df['vector_id'] = article_df['vector_id'].apply(str) \n",
"article_df.head() \n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Create an index"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"azure-cognitive-search-vector-demo created\n"
]
}
],
"source": [
"# Configure a search index\n",
"index_client = SearchIndexClient(\n",
" endpoint=search_service_endpoint, credential=credential)\n",
"fields = [\n",
" SimpleField(name=\"id\", type=SearchFieldDataType.String),\n",
" SimpleField(name=\"vector_id\", type=SearchFieldDataType.String, key=True),\n",
" SimpleField(name=\"url\", type=SearchFieldDataType.String),\n",
" SearchableField(name=\"title\", type=SearchFieldDataType.String),\n",
" SearchableField(name=\"text\", type=SearchFieldDataType.String),\n",
" SearchField(name=\"title_vector\", type=SearchFieldDataType.Collection(SearchFieldDataType.Single),\n",
" searchable=True, vector_search_dimensions=1536, vector_search_configuration=\"my-vector-config\"),\n",
" SearchField(name=\"content_vector\", type=SearchFieldDataType.Collection(SearchFieldDataType.Single),\n",
" searchable=True, vector_search_dimensions=1536, vector_search_configuration=\"my-vector-config\"),\n",
"]\n",
"\n",
"# Configure the vector search configuration\n",
"vector_search = VectorSearch(\n",
" algorithm_configurations=[\n",
" HnswVectorSearchAlgorithmConfiguration(\n",
" name=\"my-vector-config\",\n",
" kind=\"hnsw\",\n",
" parameters={\n",
" \"m\": 4,\n",
" \"efConstruction\": 400,\n",
" \"efSearch\": 500,\n",
" \"metric\": \"cosine\"\n",
" }\n",
" )\n",
" ]\n",
")\n",
"\n",
"# Optional: configure semantic reranking by passing your title, keywords, and content fields\n",
"semantic_config = SemanticConfiguration(\n",
" name=\"my-semantic-config\",\n",
" prioritized_fields=PrioritizedFields(\n",
" title_field=SemanticField(field_name=\"title\"),\n",
" prioritized_keywords_fields=[SemanticField(field_name=\"url\")],\n",
" prioritized_content_fields=[SemanticField(field_name=\"text\")]\n",
" )\n",
")\n",
"# Create the semantic settings with the configuration\n",
"semantic_settings = SemanticSettings(configurations=[semantic_config])\n",
"\n",
"# Create the index \n",
"index = SearchIndex(name=index_name, fields=fields,\n",
" vector_search=vector_search, semantic_settings=semantic_settings)\n",
"result = index_client.create_or_update_index(index)\n",
"print(f'{result.name} created')\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Insert text and embeddings into vector store\n",
"In this notebook, the wikipedia articles dataset provided by OpenAI, the embeddings are pre-computed. The code below takes the data frame and converts it into a dictionary list to upload to your Azure Search index.\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Uploaded 25000 documents in total\n"
]
}
],
"source": [
"# Convert the 'id' and 'vector_id' columns to string so one of them can serve as our key field \n",
"article_df['id'] = article_df['id'].astype(str) \n",
"article_df['vector_id'] = article_df['vector_id'].astype(str) \n",
" \n",
"# Convert the DataFrame to a list of dictionaries \n",
"documents = article_df.to_dict(orient='records') \n",
" \n",
"# Use SearchIndexingBufferedSender to upload the documents in batches optimized for indexing \n",
"with SearchIndexingBufferedSender(search_service_endpoint, index_name, AzureKeyCredential(search_service_api_key)) as batch_client: \n",
" # Add upload actions for all documents \n",
" batch_client.upload_documents(documents=documents) \n",
" \n",
"print(f\"Uploaded {len(documents)} documents in total\") "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If your dataset didn't already contain pre-computed embeddings, you can create embeddings by using the below function using the `openai` python library. You'll also notice the same function and model are being used to generate query embeddings for performing vector searches."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Content: April is the fourth month of the year in the Julian and Gregorian calendars, and comes between March\n",
"Content vector generated\n"
]
}
],
"source": [
"# Example function to generate document embedding \n",
"def generate_document_embeddings(text): \n",
" response = openai.Embedding.create( \n",
" input=text, engine=model) \n",
" embeddings = response['data'][0]['embedding'] \n",
" return embeddings \n",
" \n",
"# Sampling the first document content as an example \n",
"first_document_content = documents[0]['text'] \n",
"print(f\"Content: {first_document_content[:100]}\") \n",
" \n",
"# Generate the content vector using the `generate_document_embeddings` function \n",
"content_vector = generate_document_embeddings(first_document_content) \n",
"print(f\"Content vector generated\") \n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Perform a vector similarity search"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Title: Documenta\n",
"Score: 0.8599451\n",
"URL: https://simple.wikipedia.org/wiki/Documenta\n",
"\n",
"Title: Museum of Modern Art\n",
"Score: 0.85260946\n",
"URL: https://simple.wikipedia.org/wiki/Museum%20of%20Modern%20Art\n",
"\n",
"Title: Expressionism\n",
"Score: 0.85235393\n",
"URL: https://simple.wikipedia.org/wiki/Expressionism\n",
"\n"
]
}
],
"source": [
"# Function to generate query embedding\n",
"def generate_embeddings(text):\n",
" response = openai.Embedding.create(\n",
" input=text, engine=model)\n",
" embeddings = response['data'][0]['embedding']\n",
" return embeddings\n",
"\n",
"# Pure Vector Search\n",
"query = \"modern art in Europe\"\n",
" \n",
"search_client = SearchClient(search_service_endpoint, index_name, AzureKeyCredential(search_service_api_key)) \n",
"vector = Vector(value=generate_embeddings(query), k=3, fields=\"content_vector\") \n",
" \n",
"results = search_client.search( \n",
" search_text=None, \n",
" vectors=[vector], \n",
" select=[\"title\", \"text\", \"url\"] \n",
")\n",
" \n",
"for result in results: \n",
" print(f\"Title: {result['title']}\") \n",
" print(f\"Score: {result['@search.score']}\") \n",
" print(f\"URL: {result['url']}\\n\") "
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Perform a Hybrid Search"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Title: Wars of Scottish Independence\n",
"Score: 0.03306011110544205\n",
"URL: https://simple.wikipedia.org/wiki/Wars%20of%20Scottish%20Independence\n",
"\n",
"Title: Battle of Bannockburn\n",
"Score: 0.022253260016441345\n",
"URL: https://simple.wikipedia.org/wiki/Battle%20of%20Bannockburn\n",
"\n",
"Title: Scottish\n",
"Score: 0.016393441706895828\n",
"URL: https://simple.wikipedia.org/wiki/Scottish\n",
"\n"
]
}
],
"source": [
"# Hybrid Search\n",
"query = \"Famous battles in Scottish history\" \n",
" \n",
"search_client = SearchClient(search_service_endpoint, index_name, AzureKeyCredential(search_service_api_key)) \n",
"vector = Vector(value=generate_embeddings(query), k=3, fields=\"content_vector\") \n",
" \n",
"results = search_client.search( \n",
" search_text=query, \n",
" vectors=[vector],\n",
" select=[\"title\", \"text\", \"url\"],\n",
" top=3\n",
") \n",
" \n",
"for result in results: \n",
" print(f\"Title: {result['title']}\") \n",
" print(f\"Score: {result['@search.score']}\") \n",
" print(f\"URL: {result['url']}\\n\") "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Perform a Hybrid Search with Reranking (powered by Bing)\n",
"[Semantic search](https://learn.microsoft.com/azure/search/semantic-ranking) allows you to leverage deep neural networks from Microsoft Bing to further increase your search accuracy. Additionally, you can get captions, answers, and highlights. "
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Semantic Answer: The<em> Battle of Bannockburn,</em> fought on 23 and 24 June 1314, was an important Scottish victory in the Wars of Scottish Independence. A smaller Scottish army defeated a much larger and better armed English army. Background When King Alexander III of Scotland died in 1286, his heir was his granddaughter Margaret, Maid of Norway.\n",
"Semantic Answer Score: 0.8857421875\n",
"\n",
"Title: Wars of Scottish Independence\n",
"URL: https://simple.wikipedia.org/wiki/Wars%20of%20Scottish%20Independence\n",
"Caption: Important Figures Scotland King David II King John Balliol King Robert I the Bruce William Wallace England King Edward I King Edward II King Edward III Battles Battle of Bannockburn The Battle of Bannockburn (2324 June 1314) was an important Scottish victory. It was the decisive battle in the First War of Scottish Independence.\n",
"\n",
"Title: Battle of Bannockburn\n",
"URL: https://simple.wikipedia.org/wiki/Battle%20of%20Bannockburn\n",
"Caption: The Battle of Bannockburn, fought on 23 and 24 June 1314, was an important<em> Scottish</em> victory in the Wars of<em> Scottish</em> Independence. A smaller Scottish army defeated a much larger and better armed English army. Background When King Alexander III of Scotland died in 1286, his heir was his granddaughter Margaret, Maid of Norway.\n",
"\n",
"Title: First War of Scottish Independence\n",
"URL: https://simple.wikipedia.org/wiki/First%20War%20of%20Scottish%20Independence\n",
"Caption: The First War of<em> Scottish Independence</em> lasted from the outbreak of the war in 1296 until the 1328. The Scots were defeated at Dunbar on 27 April 1296. John Balliol abdicated in Montrose castle on 10 July 1296.\n",
"\n"
]
}
],
"source": [
"# Semantic Hybrid Search\n",
"query = \"Famous battles in Scottish history\" \n",
"\n",
"search_client = SearchClient(search_service_endpoint, index_name, AzureKeyCredential(search_service_api_key)) \n",
"vector = Vector(value=generate_embeddings(query), k=3, fields=\"content_vector\") \n",
"\n",
"results = search_client.search( \n",
" search_text=query, \n",
" vectors=[vector], \n",
" select=[\"title\", \"text\", \"url\"],\n",
" query_type=\"semantic\", query_language=\"en-us\", semantic_configuration_name='my-semantic-config', query_caption=\"extractive\", query_answer=\"extractive\",\n",
" top=3\n",
")\n",
"\n",
"semantic_answers = results.get_answers()\n",
"for answer in semantic_answers:\n",
" if answer.highlights:\n",
" print(f\"Semantic Answer: {answer.highlights}\")\n",
" else:\n",
" print(f\"Semantic Answer: {answer.text}\")\n",
" print(f\"Semantic Answer Score: {answer.score}\\n\")\n",
"\n",
"for result in results:\n",
" print(f\"Title: {result['title']}\")\n",
" print(f\"URL: {result['url']}\")\n",
" captions = result[\"@search.captions\"]\n",
" if captions:\n",
" caption = captions[0]\n",
" if caption.highlights:\n",
" print(f\"Caption: {caption.highlights}\\n\")\n",
" else:\n",
" print(f\"Caption: {caption.text}\\n\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.12"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}

@ -138,7 +138,7 @@
"metadata": {},
"outputs": [
{
"name": "stdin",
"name": "stdout",
"output_type": "stream",
"text": [
"Please enter your API Endpoint: https://4f835778-ec78-42b0-9ae3-29e3cf45b596-us-east1.apps.astra.datastax.com\n",
@ -222,7 +222,7 @@
"metadata": {},
"outputs": [
{
"name": "stdin",
"name": "stdout",
"output_type": "stream",
"text": [
"Please enter your OpenAI API Key: ········\n"
@ -251,7 +251,7 @@
"outputs": [],
"source": [
"client = openai.OpenAI(api_key=OPENAI_API_KEY)\n",
"embedding_model_name = \"text-embedding-ada-002\"\n",
"embedding_model_name = \"text-embedding-3-small\"\n",
"\n",
"result = client.embeddings.create(\n",
" input=[\n",

@ -165,7 +165,7 @@
"metadata": {},
"outputs": [
{
"name": "stdin",
"name": "stdout",
"output_type": "stream",
"text": [
"Please provide the full path to your Secure Connect Bundle zipfile: /path/to/secure-connect-DatabaseName.zip\n",
@ -406,7 +406,7 @@
"metadata": {},
"outputs": [
{
"name": "stdin",
"name": "stdout",
"output_type": "stream",
"text": [
"Please enter your OpenAI API Key: ········\n"
@ -435,7 +435,7 @@
"outputs": [],
"source": [
"client = openai.OpenAI(api_key=OPENAI_API_KEY)\n",
"embedding_model_name = \"text-embedding-ada-002\"\n",
"embedding_model_name = \"text-embedding-3-small\"\n",
"\n",
"result = client.embeddings.create(\n",
" input=[\n",

@ -145,7 +145,7 @@
"metadata": {},
"outputs": [
{
"name": "stdin",
"name": "stdout",
"output_type": "stream",
"text": [
"Please enter your Astra token ('AstraCS:...') ········\n",
@ -223,7 +223,7 @@
"metadata": {},
"outputs": [
{
"name": "stdin",
"name": "stdout",
"output_type": "stream",
"text": [
"Please enter your OpenAI API Key: ········\n"
@ -252,7 +252,7 @@
"outputs": [],
"source": [
"client = openai.OpenAI(api_key=OPENAI_API_KEY)\n",
"embedding_model_name = \"text-embedding-ada-002\"\n",
"embedding_model_name = \"text-embedding-3-small\"\n",
"\n",
"result = client.embeddings.create(\n",
" input=[\n",

@ -265,7 +265,7 @@
"import chromadb\n",
"\n",
"# I've set this to our new embeddings model, this can be changed to the embedding model of your choice\n",
"EMBEDDING_MODEL = \"text-embedding-ada-002\"\n",
"EMBEDDING_MODEL = \"text-embedding-3-small\"\n",
"\n",
"# Ignore unclosed SSL socket warnings - optional in case you get these errors\n",
"import warnings\n",

@ -306,7 +306,7 @@
"## Encode a question with OpenAI embedding model\n",
"\n",
"To perform kNN search, we need to encode queries with the same embedding model used to encode the documents at index time.\n",
"In this example, we need to use the `text-embedding-ada-002` model.\n",
"In this example, we need to use the `text-embedding-3-small` model.\n",
"\n",
"You'll need your OpenAI [API key](https://platform.openai.com/account/api-keys) to generate the embeddings."
]
@ -325,7 +325,7 @@
"openai.api_key = OPENAI_API_KEY\n",
"\n",
"# Define model\n",
"EMBEDDING_MODEL = \"text-embedding-ada-002\"\n",
"EMBEDDING_MODEL = \"text-embedding-3-small\"\n",
"\n",
"# Define question\n",
"question = 'Is the Atlantic the biggest ocean in the world?'\n",

@ -314,7 +314,7 @@
"## Encode a question with OpenAI embedding model\n",
"\n",
"To perform semantic search, we need to encode queries with the same embedding model used to encode the documents at index time.\n",
"In this example, we need to use the `text-embedding-ada-002` model.\n",
"In this example, we need to use the `text-embedding-3-small` model.\n",
"\n",
"You'll need your OpenAI [API key](https://platform.openai.com/account/api-keys) to generate the embeddings."
]
@ -333,7 +333,7 @@
"openai.api_key = OPENAI_API_KEY\n",
"\n",
"# Define model\n",
"EMBEDDING_MODEL = \"text-embedding-ada-002\"\n",
"EMBEDDING_MODEL = \"text-embedding-3-small\"\n",
"\n",
"# Define question\n",
"question = 'Is the Atlantic the biggest ocean in the world?'\n",

@ -707,7 +707,7 @@
"source": [
"## Search data\n",
"\n",
"Once the data is uploaded we will start querying the collection for the closest vectors. We may provide an additional parameter `vector_name` to switch from title to content based search. Since the precomputed embeddings were created with `text-embedding-ada-002` OpenAI model we also have to use it during search.\n"
"Once the data is uploaded we will start querying the collection for the closest vectors. We may provide an additional parameter `vector_name` to switch from title to content based search. Since the precomputed embeddings were created with `text-embedding-3-small` OpenAI model we also have to use it during search.\n"
]
},
{
@ -727,7 +727,7 @@
" # Creates embedding vector from user query\n",
" embedded_query = openai.Embedding.create(\n",
" input=query,\n",
" model=\"text-embedding-ada-002\",\n",
" model=\"text-embedding-3-small\",\n",
" )[\"data\"][0][\"embedding\"]\n",
"\n",
" # Convert the embedded_query to PostgreSQL compatible format\n",

File diff suppressed because one or more lines are too long

@ -175,7 +175,7 @@
"PORT = 19530\n",
"COLLECTION_NAME = 'movie_search'\n",
"DIMENSION = 1536\n",
"OPENAI_ENGINE = 'text-embedding-ada-002'\n",
"OPENAI_ENGINE = 'text-embedding-3-small'\n",
"openai.api_key = 'sk-your_key'\n",
"\n",
"INDEX_PARAM = {\n",

@ -275,7 +275,7 @@
"PORT = 19530\n",
"COLLECTION_NAME = 'book_search'\n",
"DIMENSION = 1536\n",
"OPENAI_ENGINE = 'text-embedding-ada-002'\n",
"OPENAI_ENGINE = 'text-embedding-3-small'\n",
"openai.api_key = 'sk-your_key'\n",
"\n",
"INDEX_PARAM = {\n",

@ -0,0 +1,6 @@
# MongoDB Atlas Vector Search
[Atlas Vector Search](https://www.mongodb.com/products/platform/atlas-vector-search) is a fully managed service that simplifies the process of effectively indexing high-dimensional vector data within MongoDB and being able to perform fast vector similarity searches. With Atlas Vector Search, you can use MongoDB as a standalone vector database for a new project or augment your existing MongoDB collections with vector search functionality. With Atlas Vector Search, you can use the powerful capabilities of vector search in any major public cloud (AWS, Azure, GCP) and achieve massive scalability and data security out of the box while being enterprise-ready with provisions like FedRamp, SoC2 compliance.
Documentation - [link](https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-overview/)

@ -0,0 +1,404 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "flQYAhphJC5m"
},
"source": [
"\n",
"This notebook demonstrates how to build a semantic search application using OpenAI and [MongoDB Atlas vector search](https://www.mongodb.com/products/platform/atlas-vector-search)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "iYMn0dXXdFbY",
"outputId": "98dab421-f11b-40b8-8f82-6de42b25725a"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Collecting pymongo\n",
" Downloading pymongo-4.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (677 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m677.1/677.1 kB\u001b[0m \u001b[31m10.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hCollecting openai\n",
" Downloading openai-1.3.3-py3-none-any.whl (220 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m220.3/220.3 kB\u001b[0m \u001b[31m24.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hCollecting dnspython<3.0.0,>=1.16.0 (from pymongo)\n",
" Downloading dnspython-2.4.2-py3-none-any.whl (300 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m300.4/300.4 kB\u001b[0m \u001b[31m29.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: anyio<4,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from openai) (3.7.1)\n",
"Requirement already satisfied: distro<2,>=1.7.0 in /usr/lib/python3/dist-packages (from openai) (1.7.0)\n",
"Collecting httpx<1,>=0.23.0 (from openai)\n",
" Downloading httpx-0.25.1-py3-none-any.whl (75 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.0/75.0 kB\u001b[0m \u001b[31m9.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: pydantic<3,>=1.9.0 in /usr/local/lib/python3.10/dist-packages (from openai) (1.10.13)\n",
"Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.10/dist-packages (from openai) (4.66.1)\n",
"Requirement already satisfied: typing-extensions<5,>=4.5 in /usr/local/lib/python3.10/dist-packages (from openai) (4.5.0)\n",
"Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.10/dist-packages (from anyio<4,>=3.5.0->openai) (3.4)\n",
"Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.10/dist-packages (from anyio<4,>=3.5.0->openai) (1.3.0)\n",
"Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<4,>=3.5.0->openai) (1.1.3)\n",
"Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->openai) (2023.7.22)\n",
"Collecting httpcore (from httpx<1,>=0.23.0->openai)\n",
" Downloading httpcore-1.0.2-py3-none-any.whl (76 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m76.9/76.9 kB\u001b[0m \u001b[31m7.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hCollecting h11<0.15,>=0.13 (from httpcore->httpx<1,>=0.23.0->openai)\n",
" Downloading h11-0.14.0-py3-none-any.whl (58 kB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.3/58.3 kB\u001b[0m \u001b[31m6.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hInstalling collected packages: h11, dnspython, pymongo, httpcore, httpx, openai\n",
"\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n",
"llmx 0.0.15a0 requires cohere, which is not installed.\n",
"llmx 0.0.15a0 requires tiktoken, which is not installed.\u001b[0m\u001b[31m\n",
"\u001b[0mSuccessfully installed dnspython-2.4.2 h11-0.14.0 httpcore-1.0.2 httpx-0.25.1 openai-1.3.3 pymongo-4.6.0\n"
]
}
],
"source": [
"!pip install pymongo openai"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "vLuKFvTwJXpu"
},
"source": [
"# Step 1: Setup the environment\n",
"\n",
"There are 2 pre-requisites for this:\n",
"\n",
"1. **MongoDB Atlas cluster**: To create a forever free MongoDB Atlas cluster, first, you need to create a MongoDB Atlas account if you don't already have one. Visit the [MongoDB Atlas website](https://www.mongodb.com/atlas/database) and click on “Register.” Visit the [MongoDB Atlas](https://account.mongodb.com/account/login) dashboard and set up your cluster. In order to take advantage of the `$vectorSearch` operator in an aggregation pipeline, you need to run MongoDB Atlas 6.0.11 or higher. This tutorial can be built using a free cluster. When youre setting up your deployment, youll be prompted to set up a database user and rules for your network connection. Please ensure you save your username and password somewhere safe and have the correct IP address rules in place so your cluster can connect properly. If you need more help getting started, check out our [tutorial on MongoDB Atlas](https://www.mongodb.com/basics/mongodb-atlas-tutorial).\n",
"\n",
"2. **OpenAI API key** To create your OpenAI key, you'll need to create an account. Once you have that, visit the [OpenAI platform](https://platform.openai.com/). Click on your profile icon in the top right of the screen to get the dropdown menu and select “View API keys”.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "qJHHIIKjIFUZ",
"outputId": "57ad72d4-8afb-4e34-aad1-1fea6eb3645b"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MongoDB Atlas Cluster URI:··········\n",
"OpenAI API Key:··········\n"
]
}
],
"source": [
"import getpass\n",
"\n",
"MONGODB_ATLAS_CLUSTER_URI = getpass.getpass(\"MongoDB Atlas Cluster URI:\")\n",
"OPENAI_API_KEY = getpass.getpass(\"OpenAI API Key:\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Sarx9wdxb4Rr"
},
"source": [
"Note: After executing the step above you will be prompted to enter the credentials."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "sk1xXoyxMfil"
},
"source": [
"For this tutorial, we will be using the\n",
"[MongoDB sample dataset](https://www.mongodb.com/docs/atlas/sample-data/). Load the sample dataset using the Atlas UI. We'll be using the “sample_mflix” database, which contains a “movies” collection where each document contains fields like title, plot, genres, cast, directors, etc.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "k-G6WhNFdIvW"
},
"outputs": [],
"source": [
"import openai\n",
"import pymongo\n",
"\n",
"client = pymongo.MongoClient(MONGODB_ATLAS_CLUSTER_URI)\n",
"db = client.sample_mflix\n",
"collection = db.movies\n",
"\n",
"openai.api_key = OPENAI_API_KEY"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "On9e13ASwReq"
},
"outputs": [],
"source": [
"ATLAS_VECTOR_SEARCH_INDEX_NAME = \"default\"\n",
"EMBEDDING_FIELD_NAME = \"embedding_openai_nov19_23\""
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "X-9gl2s-uGtw"
},
"source": [
"# Step 2: Setup embeddings generation function"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "BMnE4BxSOCtH"
},
"outputs": [],
"source": [
"model = \"text-embedding-3-small\"\n",
"def generate_embedding(text: str) -> list[float]:\n",
" return openai.embeddings.create(input = [text], model=model).data[0].embedding\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "snSjiSNKwX6Z"
},
"source": [
"# Step 3: Create and store embeddings\n",
"\n",
"Each document in the sample dataset sample_mflix.movies corresponds to a movie; we will execute an operation to create a vector embedding for the data in the \"plot\" field and store it in the database. Creating vector embeddings using OpenAI embeddings endpoint is necessary for performing a similarity search based on intent."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "t4i9gQM2xUFF",
"outputId": "ae558b67-9b06-4c83-c52a-a8047ecd40d5"
},
"outputs": [
{
"data": {
"text/plain": [
"BulkWriteResult({'writeErrors': [], 'writeConcernErrors': [], 'nInserted': 0, 'nUpserted': 0, 'nMatched': 50, 'nModified': 50, 'nRemoved': 0, 'upserted': []}, acknowledged=True)"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from pymongo import ReplaceOne\n",
"\n",
"# Update the collection with the embeddings\n",
"requests = []\n",
"\n",
"for doc in collection.find({'plot':{\"$exists\": True}}).limit(500):\n",
" doc[EMBEDDING_FIELD_NAME] = generate_embedding(doc['plot'])\n",
" requests.append(ReplaceOne({'_id': doc['_id']}, doc))\n",
"\n",
"collection.bulk_write(requests)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ShPbxQPaPvHD"
},
"source": [
"After executing the above, the documents in \"movies\" collection will contain an additional field of \"embedding\", as defined by the `EMBEDDDING_FIELD_NAME` variable, apart from already existing fields like title, plot, genres, cast, directors, etc."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Coq0tyjXyNIu"
},
"source": [
"Note: We are restricting this to just 500 documents in the interest of time. If you want to do this over the entire dataset of 23,000+ documents in our sample_mflix database, it will take a little while. Alternatively, you can use the [sample_mflix.embedded_movies collection](https://www.mongodb.com/docs/atlas/sample-data/sample-mflix/#sample_mflix.embedded_movies) which includes a pre-populated `plot_embedding` field that contains embeddings created using OpenAI's `text-embedding-3-small` embedding model that you can use with the Atlas Search vector search feature.\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "rCRCK6QOskqo"
},
"source": [
"# Step 4: Create a vector search index\n",
"\n",
"We will create Atlas Vector Search Index on this collection which will allow us to perform the Approximate KNN search, which powers the semantic search.\n",
"We will cover 2 ways to create this index - Atlas UI and using MongoDB python driver.\n",
"\n",
"(Optional) [Documentation: Create a Vector Search Index ](https://www.mongodb.com/docs/atlas/atlas-search/field-types/knn-vector/)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ymRTaFb1X5Tq"
},
"source": [
"Now head over to [Atlas UI](cloud.mongodb.com) and create an Atlas Vector Search index using the steps descibed [here](https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-tutorial/#create-the-atlas-vector-search-index). The 'dimensions' field with value 1536, corresponds to openAI text-embedding-ada002.\n",
"\n",
"Use the definition given below in the JSON editor on the Atlas UI.\n",
"\n",
"```\n",
"{\n",
" \"mappings\": {\n",
" \"dynamic\": true,\n",
" \"fields\": {\n",
" \"embedding\": {\n",
" \"dimensions\": 1536,\n",
" \"similarity\": \"dotProduct\",\n",
" \"type\": \"knnVector\"\n",
" }\n",
" }\n",
" }\n",
"}\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "2l5BzUgncjiq"
},
"source": [
"(Optional) Alternatively, we can use [pymongo driver to create these vector search indexes programatically](https://pymongo.readthedocs.io/en/stable/api/pymongo/collection.html#pymongo.collection.Collection.create_search_index)\n",
"The python command given in the cell below will create the index (this only works for the most recent version of the Python Driver for MongoDB and MongoDB server version 7.0+ Atlas cluster)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 35
},
"id": "54OWgiaPcmD0",
"outputId": "2cb9d1d8-4515-49ad-9fe7-5b4fa3c6c86b"
},
"outputs": [
{
"data": {
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
},
"text/plain": [
"'default'"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"collection.create_search_index(\n",
" {\"definition\":\n",
" {\"mappings\": {\"dynamic\": True, \"fields\": {\n",
" EMBEDDING_FIELD_NAME : {\n",
" \"dimensions\": 1536,\n",
" \"similarity\": \"dotProduct\",\n",
" \"type\": \"knnVector\"\n",
" }}}},\n",
" \"name\": ATLAS_VECTOR_SEARCH_INDEX_NAME\n",
" }\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "6V9QKgm8caNb"
},
"source": [
"# Step 5: Query your data\n",
"\n",
"The results for the query here finds movies which have semantically similar plots to the text captured in the query string, rather than being based on the keyword search.\n",
"\n",
"(Optional) [Documentation: Run Vector Search Queries](https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "34tib9TrMPg4"
},
"outputs": [],
"source": [
"\n",
"def query_results(query, k):\n",
" results = collection.aggregate([\n",
" {\n",
" '$vectorSearch': {\n",
" \"index\": ATLAS_VECTOR_SEARCH_INDEX_NAME,\n",
" \"path\": EMBEDDING_FIELD_NAME,\n",
" \"queryVector\": generate_embedding(query),\n",
" \"numCandidates\": 50,\n",
" \"limit\": 5,\n",
" }\n",
" }\n",
" ])\n",
" return results"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true,
"id": "kTqrip-hWULK"
},
"outputs": [],
"source": [
"query=\"imaginary characters from outerspace at war with earthlings\"\n",
"movies = query_results(query, 5)\n",
"\n",
"for movie in movies:\n",
" print(f'Movie Name: {movie[\"title\"]},\\nMovie Plot: {movie[\"plot\"]}\\n')"
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
},
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -73,429 +73,14 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2023-02-16T12:05:05.730338Z",
"start_time": "2023-02-16T12:05:05.723351Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"<OpenAIObject list at 0x118768f40> JSON: {\n",
" \"data\": [\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"babbage\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"davinci\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-davinci-edit-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"babbage-code-search-code\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-similarity-babbage-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"code-davinci-edit-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-davinci-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-davinci-003\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-internal\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"ada\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"babbage-code-search-text\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"babbage-similarity\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"code-search-babbage-text-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-curie-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"whisper-1\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-internal\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"code-search-babbage-code-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-ada-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-embedding-ada-002\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-internal\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-similarity-ada-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"curie-instruct-beta\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"ada-code-search-code\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"ada-similarity\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"code-search-ada-text-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-search-ada-query-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"davinci-search-document\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"gpt-3.5-turbo-0301\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"ada-code-search-text\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-search-ada-doc-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"davinci-instruct-beta\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-similarity-curie-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"code-search-ada-code-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"ada-search-query\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-search-davinci-query-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"curie-search-query\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"davinci-search-query\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"babbage-search-document\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"ada-search-document\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-search-curie-query-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-search-babbage-doc-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"curie-search-document\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-search-curie-doc-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"babbage-search-query\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-babbage-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-search-davinci-doc-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"gpt-3.5-turbo\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-search-babbage-query-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"curie-similarity\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"curie\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-similarity-davinci-001\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"text-davinci-002\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" },\n",
" {\n",
" \"created\": null,\n",
" \"id\": \"davinci-similarity\",\n",
" \"object\": \"engine\",\n",
" \"owner\": \"openai-dev\",\n",
" \"permissions\": null,\n",
" \"ready\": true\n",
" }\n",
" ],\n",
" \"object\": \"list\"\n",
"}"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"outputs": [],
"source": [
"import openai\n",
"\n",
@ -744,7 +329,7 @@
"# creates embedding vector from user query\n",
"embed = openai.Embedding.create(\n",
" input=query,\n",
" model=\"text-embedding-ada-002\",\n",
" model=\"text-embedding-3-small\",\n",
")[\"data\"][0][\"embedding\"]\n",
"\n",
"# query the database to find the top K similar content to the given query\n",

@ -74,7 +74,7 @@
"import clickhouse_connect\n",
"\n",
"# I've set this to our new embeddings model, this can be changed to the embedding model of your choice\n",
"EMBEDDING_MODEL = \"text-embedding-ada-002\"\n",
"EMBEDDING_MODEL = \"text-embedding-3-small\"\n",
"\n",
"# Ignore unclosed SSL socket warnings - optional in case you get these errors\n",
"import warnings\n",
@ -476,7 +476,7 @@
"# creates embedding vector from user query\n",
"embed = openai.Embedding.create(\n",
" input=query,\n",
" model=\"text-embedding-ada-002\",\n",
" model=\"text-embedding-3-small\",\n",
")[\"data\"][0][\"embedding\"]\n",
"\n",
"# query the database to find the top K similar content to the given query\n",

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save