Skip to main content

Why Structured Outputs?

Guaranteed Format

Always receive valid JSON matching your schema

Type Safety

Integrate with type-safe languages like TypeScript

No Parsing Errors

Eliminate JSON parsing failures and validation errors

Better Reliability

Build production applications with confidence

Basic Usage

import os
from pydantic import BaseModel
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["CHEAPESTINFERENCE_API_KEY"],
    base_url="https://api.cheapestinference.ai/v1",
)

# Define your schema
class UserInfo(BaseModel):
    name: str
    age: int
    email: str
    interests: list[str]

response = client.chat.completions.create(
    model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
    messages=[
        {"role": "user", "content": "Extract info: John is 28, email [email protected], likes hiking and photography"}
    ],
    response_format={
        "type": "json_object",
        "schema": UserInfo.model_json_schema()
    }
)

import json
data = json.loads(response.choices[0].message.content)
print(data)  # Guaranteed to match UserInfo schema

JSON Schema

Use JSON Schema to define your output format:
schema = {
    "type": "object",
    "properties": {
        "title": {"type": "string"},
        "summary": {"type": "string"},
        "tags": {
            "type": "array",
            "items": {"type": "string"}
        },
        "sentiment": {
            "type": "string",
            "enum": ["positive", "negative", "neutral"]
        }
    },
    "required": ["title", "summary", "tags", "sentiment"]
}

response = client.chat.completions.create(
    model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
    messages=[
        {"role": "user", "content": "Analyze this article: [article text]"}
    ],
    response_format={"type": "json_object", "schema": schema}
)

Common Use Cases

Data Extraction

Extract structured data from unstructured text:
from pydantic import BaseModel
from typing import List

class Article(BaseModel):
    headline: str
    author: str
    publish_date: str
    categories: List[str]
    word_count: int

response = client.chat.completions.create(
    model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
    messages=[
        {"role": "user", "content": f"Extract article metadata: {article_text}"}
    ],
    response_format={"type": "json_object", "schema": Article.model_json_schema()}
)

Classification

Classify content with structured labels:
from pydantic import BaseModel
from typing import Literal

class EmailClassification(BaseModel):
    category: Literal["spam", "urgent", "general", "promotional"]
    confidence: float
    requires_response: bool
    priority: Literal["high", "medium", "low"]

response = client.chat.completions.create(
    model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
    messages=[
        {"role": "user", "content": f"Classify this email: {email_text}"}
    ],
    response_format={"type": "json_object", "schema": EmailClassification.model_json_schema()}
)

Content Generation

Generate content with specific structure:
class BlogPost(BaseModel):
    title: str
    introduction: str
    sections: List[dict]  # [{heading: str, content: str}]
    conclusion: str
    seo_keywords: List[str]

response = client.chat.completions.create(
    model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
    messages=[
        {"role": "user", "content": "Write a blog post about sustainable living"}
    ],
    response_format={"type": "json_object", "schema": BlogPost.model_json_schema()}
)

API Response Formatting

Format LLM outputs as API responses:
class APIResponse(BaseModel):
    status: Literal["success", "error"]
    data: dict
    message: str
    timestamp: str

response = client.chat.completions.create(
    model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
    messages=[
        {"role": "user", "content": "Process user query: [query]"}
    ],
    response_format={"type": "json_object", "schema": APIResponse.model_json_schema()}
)

Complex Schemas

Nested Objects

from pydantic import BaseModel
from typing import List

class Address(BaseModel):
    street: str
    city: str
    country: str
    postal_code: str

class Company(BaseModel):
    name: str
    industry: str
    employee_count: int
    headquarters: Address
    offices: List[Address]

response = client.chat.completions.create(
    model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
    messages=[
        {"role": "user", "content": "Extract company info from: [text]"}
    ],
    response_format={"type": "json_object", "schema": Company.model_json_schema()}
)

Enums and Unions

from enum import Enum
from typing import Union

class Priority(str, Enum):
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    CRITICAL = "critical"

class Task(BaseModel):
    title: str
    description: str
    priority: Priority
    estimated_hours: Union[int, None]
    tags: List[str]

Validation Rules

from pydantic import BaseModel, Field, validator

class Product(BaseModel):
    name: str = Field(min_length=3, max_length=100)
    price: float = Field(gt=0, le=1000000)
    discount: float = Field(ge=0, le=100)
    in_stock: bool
    
    @validator('name')
    def name_must_be_capitalized(cls, v):
        if not v[0].isupper():
            raise ValueError('Name must start with capital letter')
        return v

Working with TypeScript

Zod Schemas

import { z } from 'zod';

const TaskSchema = z.object({
  id: z.string().uuid(),
  title: z.string().min(3).max(100),
  description: z.string(),
  status: z.enum(['todo', 'in_progress', 'done']),
  priority: z.number().min(1).max(5),
  assignee: z.string().email().optional(),
  due_date: z.string().datetime(),
  tags: z.array(z.string())
});

type Task = z.infer<typeof TaskSchema>;

const response = await client.chat.completions.create({
  model: 'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo',
  messages: [
    { role: 'user', content: 'Create a task for implementing user authentication' }
  ],
  response_format: {
    type: 'json_object',
    schema: TaskSchema
  }
});

const task: Task = TaskSchema.parse(JSON.parse(response.choices[0].message.content));

TypeScript Interfaces

interface ProductReview {
  product_id: string;
  rating: 1 | 2 | 3 | 4 | 5;
  title: string;
  review: string;
  pros: string[];
  cons: string[];
  would_recommend: boolean;
  verified_purchase: boolean;
}

// Convert to JSON Schema
const schema = {
  type: 'object',
  properties: {
    product_id: { type: 'string' },
    rating: { type: 'number', enum: [1, 2, 3, 4, 5] },
    title: { type: 'string' },
    review: { type: 'string' },
    pros: { type: 'array', items: { type: 'string' } },
    cons: { type: 'array', items: { type: 'string' } },
    would_recommend: { type: 'boolean' },
    verified_purchase: { type: 'boolean' }
  },
  required: ['product_id', 'rating', 'title', 'review', 'would_recommend']
};

Error Handling

Handle schema validation errors:
from pydantic import BaseModel, ValidationError

try:
    response = client.chat.completions.create(
        model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
        messages=[{"role": "user", "content": "Extract data..."}],
        response_format={"type": "json_object", "schema": MySchema.model_json_schema()}
    )
    
    data = MySchema.parse_raw(response.choices[0].message.content)
    
except ValidationError as e:
    print(f"Schema validation failed: {e}")
except json.JSONDecodeError as e:
    print(f"Invalid JSON: {e}")

Best Practices

  • Use descriptive field names
  • Add field descriptions for clarity
  • Use enums for categorical data
  • Set appropriate constraints (min, max, pattern)
  • Include example outputs in your prompt
  • Show the desired structure
  • Clarify edge cases
  • Begin with simple schemas
  • Add complexity gradually
  • Test thoroughly at each step
  • Always validate parsed JSON
  • Have fallback logic
  • Log validation failures
  • Retry with refined prompts if needed

Examples

Email Parser

class Email(BaseModel):
    sender: str
    recipient: str
    subject: str
    body: str
    action_items: List[str]
    urgency: Literal["low", "medium", "high"]
    requires_response: bool

prompt = f"""Parse this email and extract key information:
From: [email protected]
To: [email protected]
Subject: Q4 Report Due Tomorrow
Body: Please submit the Q4 financial report by EOD tomorrow. Include revenue, expenses, and projections.
"""

response = client.chat.completions.create(
    model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
    messages=[{"role": "user", "content": prompt}],
    response_format={"type": "json_object", "schema": Email.model_json_schema()}
)

Invoice Extractor

class LineItem(BaseModel):
    description: str
    quantity: int
    unit_price: float
    total: float

class Invoice(BaseModel):
    invoice_number: str
    date: str
    vendor: str
    items: List[LineItem]
    subtotal: float
    tax: float
    total: float

Survey Analyzer

class SurveyResponse(BaseModel):
    respondent_id: str
    satisfaction_score: int  # 1-10
    feedback: str
    sentiment: Literal["positive", "negative", "neutral"]
    themes: List[str]
    suggested_improvements: List[str]