Day 92 – OpenAI API Structured Outputs

For me Structured Outputs are essential when talking to an LLM. In most cases, an application needs to use data in a certain way so you want OpenAI to send back data in a specific structure, rather than having to format yourself.

Structured Outputs (SOs) ensure an OpenAI response is structured in the way you would like.

Prior to SOs you would use a specific prompt to get the response in the right structure.

Parsing a Rental Property Webpage

Let’s set a schema that will take a blurb from a Cottage Rental website and output an object …

import { NextResponse } from 'next/server';
import OpenAI from 'openai';
import { zodTextFormat } from "openai/helpers/zod";
import { z } from "zod";

export async function POST(request) {
  try {
    const client = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
      organization: process.env.OPENAI_ORG_ID,
    });

    const requestData = await request.json();
    const { model, input } = requestData;

    const PropertyListing = z.object({
      cottageName: z.string(),
      numberOfGuests: z.number(),
      numberOfBedrooms: z.number(),
      numberOfBathrooms: z.number(),
      allowsPets: z.boolean(),
      keyFeatures: z.array(z.string()),
      marketingCopy: z.string()
    });

    const response = await client.responses.parse({
      model: model || "gpt-4o-2024-08-06",
      input: input,
      text: {
        format: zodTextFormat(PropertyListing, "property"),
      },
    });

    return NextResponse.json({ 
      success: true, 
      event: response.output_parsed 
    });
  } catch (error) {
    console.error('OpenAI API Error:', error);
    return NextResponse.json(
      { success: false, error: error.message },
      { status: 500 }
    );
  }
} 

I can call this API crudely with something like:

    try {
      const response = await fetch('/api/openai/parse-cottage', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          model: "gpt-4o-2024-08-06",
          input: [
            { role: "system", content: "Extract the cottage information." },
            {
              role: "user",
              content: `webpage html here`
            },
          ],
        }),
      });

      const data = await response.json();
      
      if (!data.success) {
        throw new Error(data.error || 'Failed to parse cottage');
      }
      
      setEvent(data.event);
    } catch (err) {
      console.error('Error parsing event:', err);
      setError(err.message || 'Failed to parse cottage');
    } finally {
      setLoading(false);
    }
  };

The response I get is

{
  "cottageName": "Point Break",
  "numberOfGuests": 8,
  "numberOfBedrooms": 4,
  "numberOfBathrooms": 3,
  "allowsPets": true,
  "keyFeatures": [
    "Beachside location",
    "Contemporary interiors",
    "Part of The Dunes development",
    "Traffic-free location",
    "Open plan living",
    "Floor to ceiling windows",
    "Balcony with ocean views"
  ],
  "marketingCopy": "A beautifully finished beachside house over three floors with contemporary interiors to the highest standard. Part of The Dunes development located directly in front of the beach. Tucked away in an almost traffic-free location. Open plan living, dining and kitchen space on the 2nd floor, taking advantage of the breath-taking views over the golden sand dunes, beach and Atlantic Ocean. The spacious living area comprises of two large sofas, TV, DVD player, coffee table, floor to ceiling windows, patio doors opening onto the balcony."
}

So, what that’s done is convert some HTML into a JSON object that summarises the HTML. Useful!

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *