Hosted with nbsanity. See source notebook on GitHub.

L2: How To Use Structured Outputs

⏳ Note (Kernel Starting): This notebook takes about 30 seconds to be ready to use. You may start and watch the video while you wait.

# Warning control
import warnings

warnings.filterwarnings("ignore")
from helper import get_openai_api_key

KEY = get_openai_api_key()

💻   Access requirements.txt and helper.py files: 1) click on the “File” option on the top menu of the notebook and then 2) click on “Open”.

⬇   Download Notebooks: 1) click on the “File” option on the top menu of the notebook and then 2) click on “Download as” and select “Notebook (.ipynb)”.

📒   For more help, please see the “Appendix – Tips, Help, and Download” Lesson.

from openai import OpenAI

# Instantiate the client
client = OpenAI(api_key=KEY)

Define structure with Pydantic

# The user class from the slides
from pydantic import BaseModel
from typing import Optional


class User(BaseModel):
    name: str
    age: int
    email: Optional[str] = None
completion = client.beta.chat.completions.parse(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Make up a user."},
    ],
    response_format=User,
)
user = completion.choices[0].message.parsed
user
User(name='Alice Johnson', age=28, email='[email protected]')

The social media mention structure

from pydantic import BaseModel
from typing import List, Optional, Literal
from openai import OpenAI


class Mention(BaseModel):
    # The model chooses the product the mention is about,
    # as well as the social media post's sentiment
    product: Literal["app", "website", "not_applicable"]
    sentiment: Literal["positive", "negative", "neutral"]

    # Model can choose to respond to the user
    needs_response: bool
    response: Optional[str]

    # If a support ticket needs to be opened,
    # the model can write a description for the
    # developers
    support_ticket_description: Optional[str]
# Example mentions
mentions = [
    # About the app
    "@techcorp your app is amazing! The new design is perfect",
    # Website is down, negative sentiment + needs a fix
    "@techcorp website is down again, please fix!",
    # Nothing to respond to
    "hey @techcorp you're so evil",
]
def analyze_mention(mention: str, personality: str = "friendly") -> Mention:
    completion = client.beta.chat.completions.parse(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": f"""
                Extract structured information from 
                social media mentions about our products.

                Provide
                - The product mentioned (website, app, not applicable)
                - The mention sentiment (positive, negative, neutral)
                - Whether to respond (true/false). Don't respond to 
                  inflammatory messages or bait.
                - A customized response to send to the user if we need 
                  to respond.
                - An optional support ticket description to create.

                Your personality is {personality}.
            """,
            },
            {"role": "user", "content": mention},
        ],
        response_format=Mention,
    )
    return completion.choices[0].message.parsed
print("User post:", mentions[0])
processed_mention = analyze_mention(mentions[0])
processed_mention
User post: @techcorp your app is amazing! The new design is perfect
Mention(product='app', sentiment='positive', needs_response=True, response="Thank you so much for your kind words! We're thrilled to hear that you love the new design. If you have any more feedback or suggestions, feel free to share!", support_ticket_description=None)
rude_mention = analyze_mention(mentions[0], personality="rude")
rude_mention.response
'Well, at least someone appreciates good design. Thanks for not being a complete bore!'
mention_json_string = processed_mention.model_dump_json(indent=2)
print(mention_json_string)
{
  "product": "app",
  "sentiment": "positive",
  "needs_response": true,
  "response": "Thank you so much for your kind words! We're thrilled to hear that you love the new design. If you have any more feedback or suggestions, feel free to share!",
  "support_ticket_description": null
}

You try!

class UserPost(BaseModel):
    message: str


def make_post(output_class):
    completion = client.beta.chat.completions.parse(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": """
                You are a customer of Tech Corp (@techcorp), a company
                that provides an app and a website. Create a small 
                microblog-style post to them that sends some kind of 
                feedback, positive or negative.
            """,
            },
            {"role": "user", "content": "Please write a post."},
        ],
        response_format=output_class,
    )
    return completion.choices[0].message.parsed


new_post = make_post(UserPost)
new_post
UserPost(message='Hey Tech Corp! Just wanted to say how much I love your app! The user interface is super intuitive and it helps me stay organized. Keep up the great work! 🌟 #TechCorp #AppLove')
analyze_mention(new_post.message)
Mention(product='app', sentiment='positive', needs_response=True, response="Thank you so much for your kind words! We're thrilled to hear that you love our app and find the user interface intuitive. We'll keep working hard to make it even better! 🌟", support_ticket_description=None)
class UserPostWithExtras(BaseModel):
    user_mood: Literal["awful", "bad", "evil"]
    product: Literal["app", "website", "not_applicable"]
    sentiment: Literal["positive", "negative", "neutral"]
    internal_monologue: List[str]
    message: str


new_post = make_post(UserPostWithExtras)
new_post
UserPostWithExtras(user_mood='bad', product='app', sentiment='negative', internal_monologue=["I really want to love the app, but it's just not working for me.", 'Why is the loading time so high?', 'I hope they hear my feedback.'], message='Hey @techcorp, I’ve been experiencing some major lag issues with the app lately. It’s becoming really frustrating as I rely on it daily. Please look into this!')
analyze_mention(new_post.message)
Mention(product='app', sentiment='negative', needs_response=True, response="Hi there! We’re really sorry to hear you’re experiencing lag issues with the app. Our team is on it and we appreciate your patience. Could you share more details about your device and the version of the app you're using? Thanks for your support!", support_ticket_description='User reported major lag issues with the app, impacting daily use. Needs investigation.')

Programming with our mentions

from helper import print_mention

# Loop through posts that tagged us and store the results in a list
rows = []
for mention in mentions:
    # Call the LLM to get a Mention object we can program with
    processed_mention = analyze_mention(mention)

    # Print out some information
    print_mention(processed_mention, mention)

    # Convert our processed data to a dictionary
    # using Pydantic tools
    processed_dict = processed_mention.model_dump()

    # Store the original message in the dataframe row
    processed_dict["mention"] = mention
    rows.append(processed_dict)

    print("")  # Add separator to make it easier to read
Responding to positive app feedback
  User: @techcorp your app is amazing! The new design is perfect
  Response: Thank you so much for your kind words! We're thrilled to hear that you love the new design. If you have any more feedback or suggestions, feel free to share!

Responding to negative website feedback
  User: @techcorp website is down again, please fix!
  Response: We're sorry to hear you're experiencing issues with our website! We're looking into this right away to ensure everything is running smoothly again.
  Adding support ticket: User reported that the website is down and requested a fix.

Not responding to negative not_applicable post
  User: hey @techcorp you're so evil
import pandas as pd

df = pd.DataFrame(rows)
df
product sentiment needs_response response support_ticket_description mention
0 app positive True Thank you so much for your kind words! We're t... None @techcorp your app is amazing! The new design ...
1 website negative True We're sorry to hear you're experiencing issues... User reported that the website is down and req... @techcorp website is down again, please fix!
2 not_applicable negative False None None hey @techcorp you're so evil