Skip to content

Instantly share code, notes, and snippets.

@donvito
Last active August 15, 2024 06:13
Show Gist options
  • Save donvito/d3927c09029770592b5644f04942b811 to your computer and use it in GitHub Desktop.
Save donvito/d3927c09029770592b5644f04942b811 to your computer and use it in GitHub Desktop.
OpenAI Structured Output Example - Study Plan Generator
from pydantic import BaseModel
from typing import List, Optional
from openai import OpenAI
import json
from pydantic import BaseModel
from typing import List, Optional
from output_generators import generate_html, generate_pdf
class Lesson(BaseModel):
title: str
description: str
duration_minutes: int
class Module(BaseModel):
title: str
description: str
objectives: List[str]
lessons: List[Lesson]
class StudyPlan(BaseModel):
name: str
start_date: str
end_date: str
notes: List[str]
modules: List[Module]
client = OpenAI()
completion = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
max_tokens=1024,
temperature=0,
messages=[
{"role": "system", "content": "You are a helpful math tutor and is an expert in creating study plans for homeschooling kids."},
{"role": "user", "content": "create a study plan to learn Algebra basics to my 14 year old"},
],
response_format=StudyPlan,
)
event = completion.choices[0].message.parsed
# Convert the Pydantic model instance to JSON (without indent)
json_string = event.json()
# Validate the JSON string against the StudyPlan schema
try:
study_plan_dict = json.loads(json_string)
study_plan = StudyPlan.model_validate(study_plan_dict)
print("The JSON string is valid and adheres to the StudyPlan schema.")
except Exception as e:
print("Validation error:", e)
# Format JSON string with indentation using the built-in json module
formatted_json = json.dumps(json.loads(json_string), indent=4)
with open("study_plan.json", 'w') as json_file:
json_file.write(formatted_json)
print(formatted_json)
generate_html(formatted_json, "study_plan.html")
generate_pdf(formatted_json, "study_plan.pdf")
from fpdf import FPDF
from typing import Dict
import json
from pathlib import Path
def generate_html(json_data: str, output_file: str) -> None:
# Parse JSON data
study_plan = json.loads(json_data)
# Start building the HTML content with CSS styling
html_content = f"""
<html>
<head>
<title>{study_plan['name']}</title>
<style>
body {{
font-family: 'Arial', sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
line-height: 1.6;
}}
.container {{
max-width: 800px;
margin: auto;
background: white;
padding: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}}
h1 {{
color: #333;
font-size: 24px;
border-bottom: 2px solid #ddd;
padding-bottom: 10px;
}}
h2 {{
color: #555;
font-size: 20px;
margin-top: 40px;
border-bottom: 1px solid #ddd;
padding-bottom: 10px;
}}
h3 {{
color: #666;
font-size: 18px;
margin-top: 20px;
}}
p {{
color: #444;
}}
ul {{
list-style-type: disc;
padding-left: 20px;
color: #444;
}}
li {{
margin-bottom: 10px;
}}
.module {{
margin-bottom: 30px;
padding: 10px;
background-color: #f9f9f9;
}}
.lesson {{
margin-left: 20px;
padding-left: 10px;
}}
.label {{
font-weight: bold;
color: #333;
margin-top: 10px;
}}
</style>
</head>
<body>
<div class="container">
<h1>Study Plan: {study_plan['name']}</h1>
<p><strong>Start Date:</strong> {study_plan['start_date']}</p>
<p><strong>End Date:</strong> {study_plan['end_date']}</p>
<h2>Notes</h2>
<ul>
"""
for note in study_plan['notes']:
html_content += f"<li>{note}</li>"
html_content += "</ul><h2>Modules</h2>"
for module in study_plan['modules']:
html_content += f"""
<div class="module">
<h3>{module['title']}</h3>
<p>{module['description']}</p>
<div class="label">Objectives</div>
<ul>
"""
for objective in module['objectives']:
html_content += f"<li>{objective}</li>"
html_content += "</ul> <div class='label'>Lessons</div><div class='lesson'><ul>"
for lesson in module['lessons']:
html_content += f"""
<li>
<strong>{lesson['title']}</strong><br>
{lesson['description']}<br>
<div class="label">Duration:</div>
<em>{lesson['duration_minutes']} minutes</em>
</li>
"""
html_content += "</ul></div></div>"
html_content += """
</div>
</body>
</html>
"""
# Write HTML content to the specified output file
with open(output_file, 'w') as file:
file.write(html_content)
def generate_pdf(json_data: str, output_file: str) -> None:
# Parse JSON data
study_plan = json.loads(json_data)
# Create PDF document
pdf = FPDF()
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()
# Add title
pdf.set_font("Arial", 'B', 16)
pdf.cell(0, 10, f"Study Plan: {study_plan['name']}", 0, 1, 'C')
# Add study plan details
pdf.set_font("Arial", '', 12)
pdf.cell(0, 10, f"Start Date: {study_plan['start_date']}", 0, 1)
pdf.cell(0, 10, f"End Date: {study_plan['end_date']}", 0, 1)
# Add notes
pdf.set_font("Arial", 'B', 14)
pdf.cell(0, 10, "Notes:", 0, 1)
pdf.set_font("Arial", '', 12)
for note in study_plan['notes']:
pdf.multi_cell(0, 10, f"- {note}")
# Add modules with visual differentiation
pdf.set_font("Arial", 'B', 14)
pdf.cell(0, 10, "Modules:", 0, 1)
for module in study_plan['modules']:
pdf.set_font("Arial", 'B', 12)
pdf.set_fill_color(240, 240, 240) # Light gray background for module title
pdf.cell(0, 10, f"Module Title: {module['title']}", 0, 1, 'L', fill=True)
pdf.set_font("Arial", '', 12)
pdf.multi_cell(0, 10, f"Description: {module['description']}")
pdf.set_font("Arial", 'I', 12)
pdf.cell(0, 10, "Objectives:", 0, 1)
for objective in module['objectives']:
pdf.multi_cell(0, 10, f"- {objective}")
pdf.set_font("Arial", '', 12)
pdf.cell(0, 10, "Lessons:", 0, 1)
for lesson in module['lessons']:
pdf.set_fill_color(255, 240, 240) # Slightly colored background for lessons
pdf.cell(0, 10, f" Lesson Title: {lesson['title']}", 0, 1, 'L', fill=True)
pdf.multi_cell(0, 10, f" Description: {lesson['description']}")
pdf.cell(0, 10, f" Duration: {lesson['duration_minutes']} minutes", 0, 1)
pdf.ln(5)
pdf.ln(10)
# Save the PDF to the specified output file
pdf.output(output_file)
annotated-types==0.7.0
anyio==4.4.0
certifi==2024.7.4
distro==1.9.0
fpdf==1.7.2
h11==0.14.0
httpcore==1.0.5
httpx==0.27.0
idna==3.7
jiter==0.5.0
openai==1.40.6
pydantic==2.8.2
pydantic_core==2.20.1
sniffio==1.3.1
tqdm==4.66.5
typing_extensions==4.12.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment