Skip to main content

Overview

Structured Outputs ensure that model responses strictly adhere to a predefined JSON schema. This is essential for building reliable integrations where you need consistent, parseable data structures.

Response Format Types

The Dedalus SDK supports three response format types:
  • text - Free-form text response (default)
  • json_object - Freeform JSON object
  • json_schema - JSON response conforming to a specific schema

Basic JSON Object

Request a JSON response without a specific schema:
import Dedalus from 'dedalus-labs';

const client = new Dedalus();

const completion = await client.chat.completions.create({
  model: 'openai/gpt-5-nano',
  messages: [
    { 
      role: 'user', 
      content: 'Extract the name and email from: John Doe ([email protected])' 
    },
  ],
  response_format: { type: 'json_object' },
});

const data = JSON.parse(completion.choices[0].message.content);
console.log(data); // { "name": "John Doe", "email": "[email protected]" }
When using json_object, you should include the word “JSON” in your prompt to guide the model.

JSON Schema

Define a strict schema that the model must follow:
import Dedalus from 'dedalus-labs';

const client = new Dedalus();

const completion = await client.chat.completions.create({
  model: 'openai/gpt-5-nano',
  messages: [
    { role: 'user', content: 'Extract user info from: Jane Smith, age 28, email [email protected]' },
  ],
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'user_info',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          age: { type: 'number' },
          email: { type: 'string', format: 'email' },
        },
        required: ['name', 'age', 'email'],
        additionalProperties: false,
      },
    },
  },
});

const userData = JSON.parse(completion.choices[0].message.content);
console.log(userData);
// { "name": "Jane Smith", "age": 28, "email": "[email protected]" }

TypeScript Types

You can define TypeScript interfaces and use them with structured outputs:
import Dedalus from 'dedalus-labs';

interface Product {
  name: string;
  price: number;
  category: string;
  inStock: boolean;
  tags: string[];
}

const client = new Dedalus();

const completion = await client.chat.completions.create({
  model: 'openai/gpt-5-nano',
  messages: [
    { 
      role: 'user', 
      content: 'Extract product info: MacBook Pro, $1999, Electronics, Available, [laptop, computer, apple]' 
    },
  ],
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'product_extraction',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          price: { type: 'number' },
          category: { type: 'string' },
          inStock: { type: 'boolean' },
          tags: { 
            type: 'array',
            items: { type: 'string' }
          },
        },
        required: ['name', 'price', 'category', 'inStock', 'tags'],
        additionalProperties: false,
      },
    },
  },
});

const product: Product = JSON.parse(completion.choices[0].message.content);
console.log(product.name, product.price);

Complex Nested Schemas

Define complex nested object structures:
import Dedalus from 'dedalus-labs';

const client = new Dedalus();

const completion = await client.chat.completions.create({
  model: 'openai/gpt-5-nano',
  messages: [
    { 
      role: 'user', 
      content: 'Create a sample order for customer John Doe with 2 items' 
    },
  ],
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'order',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          orderId: { type: 'string' },
          customer: {
            type: 'object',
            properties: {
              name: { type: 'string' },
              email: { type: 'string' },
            },
            required: ['name', 'email'],
            additionalProperties: false,
          },
          items: {
            type: 'array',
            items: {
              type: 'object',
              properties: {
                productId: { type: 'string' },
                quantity: { type: 'number' },
                price: { type: 'number' },
              },
              required: ['productId', 'quantity', 'price'],
              additionalProperties: false,
            },
          },
          totalAmount: { type: 'number' },
        },
        required: ['orderId', 'customer', 'items', 'totalAmount'],
        additionalProperties: false,
      },
    },
  },
});

const order = JSON.parse(completion.choices[0].message.content);
console.log(order);

Using Zod for Schema Validation

Combine structured outputs with Zod for runtime validation:
import Dedalus from 'dedalus-labs';
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';

// Define Zod schema
const UserSchema = z.object({
  name: z.string(),
  age: z.number().min(0).max(120),
  email: z.string().email(),
  preferences: z.object({
    newsletter: z.boolean(),
    notifications: z.boolean(),
  }),
});

type User = z.infer<typeof UserSchema>;

const client = new Dedalus();

const completion = await client.chat.completions.create({
  model: 'openai/gpt-5-nano',
  messages: [
    { role: 'user', content: 'Create a user profile for Alice, 25 years old' },
  ],
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'user_profile',
      strict: true,
      schema: zodToJsonSchema(UserSchema),
    },
  },
});

const rawData = JSON.parse(completion.choices[0].message.content);

// Validate with Zod
const user = UserSchema.parse(rawData);
console.log(user);

Enum Values

Constrain string values to specific options:
import Dedalus from 'dedalus-labs';

const client = new Dedalus();

const completion = await client.chat.completions.create({
  model: 'openai/gpt-5-nano',
  messages: [
    { role: 'user', content: 'Classify the sentiment of: I love this product!' },
  ],
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'sentiment_analysis',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          sentiment: {
            type: 'string',
            enum: ['positive', 'negative', 'neutral'],
          },
          confidence: {
            type: 'number',
            minimum: 0,
            maximum: 1,
          },
        },
        required: ['sentiment', 'confidence'],
        additionalProperties: false,
      },
    },
  },
});

const result = JSON.parse(completion.choices[0].message.content);
console.log(result); // { "sentiment": "positive", "confidence": 0.95 }

Array Responses

Generate arrays of structured objects:
import Dedalus from 'dedalus-labs';

const client = new Dedalus();

const completion = await client.chat.completions.create({
  model: 'openai/gpt-5-nano',
  messages: [
    { role: 'user', content: 'Generate 3 sample blog post ideas about AI' },
  ],
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'blog_ideas',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          posts: {
            type: 'array',
            items: {
              type: 'object',
              properties: {
                title: { type: 'string' },
                description: { type: 'string' },
                tags: {
                  type: 'array',
                  items: { type: 'string' },
                },
              },
              required: ['title', 'description', 'tags'],
              additionalProperties: false,
            },
          },
        },
        required: ['posts'],
        additionalProperties: false,
      },
    },
  },
});

const ideas = JSON.parse(completion.choices[0].message.content);
console.log(ideas.posts);

Streaming with Structured Outputs

Structured outputs work with streaming, but you’ll need to accumulate the chunks and parse the complete JSON at the end.
import Dedalus from 'dedalus-labs';

const client = new Dedalus();

const stream = await client.chat.completions.create({
  model: 'openai/gpt-5-nano',
  stream: true,
  messages: [
    { role: 'user', content: 'Generate user data for Bob' },
  ],
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'user',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          age: { type: 'number' },
        },
        required: ['name', 'age'],
        additionalProperties: false,
      },
    },
  },
});

let content = '';
for await (const chunk of stream) {
  const delta = chunk.choices[0]?.delta?.content;
  if (delta) {
    content += delta;
  }
}

const userData = JSON.parse(content);
console.log(userData);

Error Handling

import Dedalus from 'dedalus-labs';

const client = new Dedalus();

try {
  const completion = await client.chat.completions.create({
    model: 'openai/gpt-5-nano',
    messages: [{ role: 'user', content: 'Extract data' }],
    response_format: {
      type: 'json_schema',
      json_schema: {
        name: 'data',
        strict: true,
        schema: {
          type: 'object',
          properties: {
            value: { type: 'string' },
          },
          required: ['value'],
          additionalProperties: false,
        },
      },
    },
  });

  const data = JSON.parse(completion.choices[0].message.content);
  console.log(data);
} catch (error) {
  if (error instanceof Dedalus.APIError) {
    console.error('API Error:', error.status, error.message);
  } else if (error instanceof SyntaxError) {
    console.error('Invalid JSON response:', error.message);
  } else {
    console.error('Unexpected error:', error);
  }
}

Best Practices

1

Use strict mode

Always set strict: true in your JSON schema to ensure exact compliance.
2

Set additionalProperties to false

Prevent unexpected fields by setting additionalProperties: false in your schema.
3

Provide clear prompts

Give clear instructions about what data to extract or generate.
4

Validate responses

Always validate and handle potential JSON parsing errors.
5

Use appropriate models

Not all models support structured outputs equally well. Use newer models for best results.
Structured outputs work best with models that support function calling and JSON mode. Check the model’s capabilities before use.

Build docs developers (and LLMs) love