441 lines
12 KiB
TypeScript
441 lines
12 KiB
TypeScript
/**
|
|
* OpenAI Structured Outputs - JSON Schema Examples
|
|
*
|
|
* This template demonstrates:
|
|
* - JSON schema with strict mode
|
|
* - Complex nested schemas
|
|
* - Type-safe responses
|
|
* - Validation patterns
|
|
* - Common use cases (extraction, classification, formatting)
|
|
*/
|
|
|
|
import OpenAI from 'openai';
|
|
|
|
const openai = new OpenAI({
|
|
apiKey: process.env.OPENAI_API_KEY,
|
|
});
|
|
|
|
// =============================================================================
|
|
// BASIC STRUCTURED OUTPUT
|
|
// =============================================================================
|
|
|
|
async function basicStructuredOutput() {
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4o', // Best for structured outputs
|
|
messages: [
|
|
{ role: 'user', content: 'Generate a person profile' }
|
|
],
|
|
response_format: {
|
|
type: 'json_schema',
|
|
json_schema: {
|
|
name: 'person_profile',
|
|
strict: true,
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
name: { type: 'string' },
|
|
age: { type: 'number' },
|
|
email: { type: 'string' },
|
|
},
|
|
required: ['name', 'age', 'email'],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const person = JSON.parse(completion.choices[0].message.content!);
|
|
console.log('Person:', person);
|
|
|
|
return person;
|
|
}
|
|
|
|
// =============================================================================
|
|
// COMPLEX NESTED SCHEMA
|
|
// =============================================================================
|
|
|
|
async function complexSchema() {
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4o',
|
|
messages: [
|
|
{ role: 'user', content: 'Generate a company organizational structure' }
|
|
],
|
|
response_format: {
|
|
type: 'json_schema',
|
|
json_schema: {
|
|
name: 'org_structure',
|
|
strict: true,
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
company: { type: 'string' },
|
|
founded: { type: 'number' },
|
|
departments: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
name: { type: 'string' },
|
|
head: { type: 'string' },
|
|
employees: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
name: { type: 'string' },
|
|
role: { type: 'string' },
|
|
years: { type: 'number' },
|
|
},
|
|
required: ['name', 'role', 'years'],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
},
|
|
required: ['name', 'head', 'employees'],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
},
|
|
required: ['company', 'founded', 'departments'],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const org = JSON.parse(completion.choices[0].message.content!);
|
|
console.log('Organization:', JSON.stringify(org, null, 2));
|
|
|
|
return org;
|
|
}
|
|
|
|
// =============================================================================
|
|
// DATA EXTRACTION
|
|
// =============================================================================
|
|
|
|
async function extractData() {
|
|
const text = `
|
|
John Doe is a 35-year-old software engineer living in San Francisco.
|
|
He works at TechCorp and has been there for 5 years.
|
|
His email is john.doe@example.com and his phone is (555) 123-4567.
|
|
`;
|
|
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4o',
|
|
messages: [
|
|
{
|
|
role: 'system',
|
|
content: 'Extract structured information from the provided text.',
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: text,
|
|
},
|
|
],
|
|
response_format: {
|
|
type: 'json_schema',
|
|
json_schema: {
|
|
name: 'extracted_info',
|
|
strict: true,
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
name: { type: 'string' },
|
|
age: { type: 'number' },
|
|
occupation: { type: 'string' },
|
|
location: { type: 'string' },
|
|
company: { type: 'string' },
|
|
tenure_years: { type: 'number' },
|
|
contact: {
|
|
type: 'object',
|
|
properties: {
|
|
email: { type: 'string' },
|
|
phone: { type: 'string' },
|
|
},
|
|
required: ['email', 'phone'],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
required: ['name', 'age', 'occupation', 'location', 'company', 'tenure_years', 'contact'],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const extracted = JSON.parse(completion.choices[0].message.content!);
|
|
console.log('Extracted:', JSON.stringify(extracted, null, 2));
|
|
|
|
return extracted;
|
|
}
|
|
|
|
// =============================================================================
|
|
// CLASSIFICATION
|
|
// =============================================================================
|
|
|
|
async function classifyText() {
|
|
const text = 'This product is absolutely terrible. It broke after one day of use. Very disappointed!';
|
|
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4o',
|
|
messages: [
|
|
{
|
|
role: 'system',
|
|
content: 'Classify the sentiment and extract key information from product reviews.',
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: text,
|
|
},
|
|
],
|
|
response_format: {
|
|
type: 'json_schema',
|
|
json_schema: {
|
|
name: 'review_classification',
|
|
strict: true,
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
sentiment: {
|
|
type: 'string',
|
|
enum: ['positive', 'negative', 'neutral'],
|
|
},
|
|
confidence: { type: 'number' },
|
|
category: {
|
|
type: 'string',
|
|
enum: ['product_quality', 'customer_service', 'shipping', 'pricing', 'other'],
|
|
},
|
|
issues: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
},
|
|
rating_estimate: { type: 'number' },
|
|
},
|
|
required: ['sentiment', 'confidence', 'category', 'issues', 'rating_estimate'],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const classification = JSON.parse(completion.choices[0].message.content!);
|
|
console.log('Classification:', JSON.stringify(classification, null, 2));
|
|
|
|
return classification;
|
|
}
|
|
|
|
// =============================================================================
|
|
// SIMPLE JSON MODE (Without Strict Schema)
|
|
// =============================================================================
|
|
|
|
async function simpleJsonMode() {
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-5',
|
|
messages: [
|
|
{ role: 'user', content: 'List 3 programming languages and their use cases as JSON' }
|
|
],
|
|
response_format: { type: 'json_object' },
|
|
});
|
|
|
|
const data = JSON.parse(completion.choices[0].message.content!);
|
|
console.log('JSON output:', data);
|
|
|
|
return data;
|
|
}
|
|
|
|
// =============================================================================
|
|
// ENUM VALUES
|
|
// =============================================================================
|
|
|
|
async function withEnums() {
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4o',
|
|
messages: [
|
|
{ role: 'user', content: 'Categorize this as a bug report: The app crashes on startup' }
|
|
],
|
|
response_format: {
|
|
type: 'json_schema',
|
|
json_schema: {
|
|
name: 'issue_categorization',
|
|
strict: true,
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
type: {
|
|
type: 'string',
|
|
enum: ['bug', 'feature_request', 'question', 'documentation'],
|
|
},
|
|
severity: {
|
|
type: 'string',
|
|
enum: ['critical', 'high', 'medium', 'low'],
|
|
},
|
|
component: {
|
|
type: 'string',
|
|
enum: ['frontend', 'backend', 'database', 'infrastructure', 'unknown'],
|
|
},
|
|
},
|
|
required: ['type', 'severity', 'component'],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const categorization = JSON.parse(completion.choices[0].message.content!);
|
|
console.log('Categorization:', categorization);
|
|
|
|
return categorization;
|
|
}
|
|
|
|
// =============================================================================
|
|
// VALIDATION EXAMPLE
|
|
// =============================================================================
|
|
|
|
function validateSchema<T>(data: any, expectedFields: string[]): T {
|
|
for (const field of expectedFields) {
|
|
if (!(field in data)) {
|
|
throw new Error(`Missing required field: ${field}`);
|
|
}
|
|
}
|
|
|
|
return data as T;
|
|
}
|
|
|
|
async function withValidation() {
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4o',
|
|
messages: [
|
|
{ role: 'user', content: 'Generate a user profile with name and email' }
|
|
],
|
|
response_format: {
|
|
type: 'json_schema',
|
|
json_schema: {
|
|
name: 'user_profile',
|
|
strict: true,
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
name: { type: 'string' },
|
|
email: { type: 'string' },
|
|
},
|
|
required: ['name', 'email'],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const raw = JSON.parse(completion.choices[0].message.content!);
|
|
|
|
// Validate before using
|
|
interface UserProfile {
|
|
name: string;
|
|
email: string;
|
|
}
|
|
|
|
const validated = validateSchema<UserProfile>(raw, ['name', 'email']);
|
|
console.log('Validated user:', validated);
|
|
|
|
return validated;
|
|
}
|
|
|
|
// =============================================================================
|
|
// BATCH EXTRACTION
|
|
// =============================================================================
|
|
|
|
async function batchExtraction(texts: string[]) {
|
|
const results = [];
|
|
|
|
for (const text of texts) {
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4o',
|
|
messages: [
|
|
{ role: 'system', content: 'Extract key information as structured data' },
|
|
{ role: 'user', content: text },
|
|
],
|
|
response_format: {
|
|
type: 'json_schema',
|
|
json_schema: {
|
|
name: 'extracted_data',
|
|
strict: true,
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
summary: { type: 'string' },
|
|
key_points: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
},
|
|
},
|
|
required: ['summary', 'key_points'],
|
|
additionalProperties: false,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const extracted = JSON.parse(completion.choices[0].message.content!);
|
|
results.push({ text, extracted });
|
|
|
|
// Rate limit protection
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
}
|
|
|
|
console.log(`Extracted ${results.length} items`);
|
|
return results;
|
|
}
|
|
|
|
// =============================================================================
|
|
// MAIN EXECUTION
|
|
// =============================================================================
|
|
|
|
async function main() {
|
|
console.log('=== OpenAI Structured Outputs Examples ===\n');
|
|
|
|
// Example 1: Basic
|
|
console.log('1. Basic Structured Output:');
|
|
await basicStructuredOutput();
|
|
console.log();
|
|
|
|
// Example 2: Complex schema
|
|
console.log('2. Complex Nested Schema:');
|
|
await complexSchema();
|
|
console.log();
|
|
|
|
// Example 3: Data extraction
|
|
console.log('3. Data Extraction:');
|
|
await extractData();
|
|
console.log();
|
|
|
|
// Example 4: Classification
|
|
console.log('4. Text Classification:');
|
|
await classifyText();
|
|
console.log();
|
|
|
|
// Example 5: Simple JSON mode
|
|
console.log('5. Simple JSON Mode:');
|
|
await simpleJsonMode();
|
|
console.log();
|
|
|
|
// Example 6: Enums
|
|
console.log('6. Enum Values:');
|
|
await withEnums();
|
|
console.log();
|
|
}
|
|
|
|
// Run if executed directly
|
|
if (require.main === module) {
|
|
main().catch(console.error);
|
|
}
|
|
|
|
export {
|
|
basicStructuredOutput,
|
|
complexSchema,
|
|
extractData,
|
|
classifyText,
|
|
simpleJsonMode,
|
|
withEnums,
|
|
withValidation,
|
|
batchExtraction,
|
|
};
|