Last active
August 15, 2024 06:13
-
-
Save donvito/d3927c09029770592b5644f04942b811 to your computer and use it in GitHub Desktop.
OpenAI Structured Output Example - Study Plan Generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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