Show issues from all time

Summary

This PR adds comprehensive support for propertyOrdering in the Google provider, allowing developers to control the order of properties in JSON responses from Gemini models. The implementation supports both a simple array format for basic use cases and a nested object format for complex structures.

Usage Examples

Simple Array Format

For basic root-level property ordering, you can use a simple array:

import { google } from '@ai-sdk/google';
import { generateObject } from 'ai';
import { z } from 'zod';

const result = await generateObject({
  model: google('gemini-2.0-flash'),
  providerOptions: {
    google: {
      propertyOrdering: ['name', 'age', 'email'], // Clean and simple!
    },
  },
  schema: z.object({
    name: z.string(),
    age: z.number(),
    email: z.string(),
  }),
  prompt: 'Generate a person profile',
});

// Output will have properties in the specified order:
// { "name": "...", "age": ..., "email": "..." }

Complex Nested Object Ordering

For nested structures, use the object format with dot notation:

const result = await generateObject({
  model: google('gemini-2.0-flash'),
  providerOptions: {
    google: {
      propertyOrdering: {
        '': ['name', 'profile', 'preferences'], // Empty string for root
        'profile': ['bio', 'settings', 'contacts'],
        'profile.settings': ['theme', 'notifications'], // dot-notation for nested obejcts
        'profile.settings.notifications': ['email', 'push'],
        'profile.contacts': ['address', 'phone'],
        'profile.contacts.address': ['street', 'city'],
        'preferences': ['language', 'timezone'],
      },
    },
  },
  schema: z.object({
    name: z.string(),
    profile: z.object({
      bio: z.string(),
      settings: z.object({
        theme: z.string(),
        notifications: z.object({
          email: z.boolean(),
          push: z.boolean(),
        }),
      }),
      contacts: z.object({
        address: z.object({
          street: z.string(),
          city: z.string(),
        }),
        phone: z.string(),
      }),
    }),
    preferences: z.object({
      language: z.string(),
      timezone: z.string(),
    }),
  }),
  prompt: 'Generate a comprehensive user profile',
});

Technical Implementation

Key Changes

  1. Enhanced Type System:

    // Supports both formats:
    propertyOrdering: z.union([
      z.array(z.string()), // Simple: ['name', 'age', 'email']
      z.record(z.string(), z.array(z.string())), // Nested: { '': [...], 'profile': [...] }
    ]).optional();
    
  2. Smart Format Normalization:

    • Automatically converts simple array format to object format internally
    • Maintains full backward compatibility
    • Enables clean API for common use cases
  3. Enhanced convertJSONSchemaToOpenAPISchema function:

    • Support for both Record<string, string[]> and string[] formats
      • string[] for simple root-level property ordering and Record<string, string[]> for nested property ordering.
    • Internal normalization logic to convert between formats
    • Recursive property ordering for nested objects
    • Path tracking using dot notation ('parent.child')
  4. OpenAPI Schema Generation: The function converts property ordering definitions into OpenAPI-compliant schemas:

    // Input
    { '': ['name', 'profile'], 'profile': ['bio', 'settings'] }
    
    // Generated OpenAPI Schema
    {
      "type": "object",
      "properties": { /* ... */ },
      "propertyOrdering": ["name", "profile"]  // ← Automatically included
    }
    

Path-based Property Ordering

The implementation supports:

  • Root level: '' (empty string) or simple array format
  • Nested objects: 'parent.child' dot notation

Testing

  • 18/18 tests passing
  • ✅ Test coverage for both array and object formats
  • ✅ Tests for nested object property ordering
  • ✅ Tests for backward compatibility
  • ✅ Edge cases handled (arrays, allOf, anyOf, oneOf)
  • ✅ All existing functionality preserved

Test Cases Added:

  1. should add propertyOrdering when provided - Complex nested object test
  2. should work without propertyOrdering - Backward compatibility test
  3. should support simple array format for root-level property ordering - Simple array format test

Documentation Updates

README.md

  • Comprehensive "Property Ordering for Structured Output" section
  • Examples for both simple array and object formats
  • Real-world usage examples
  • Links to Google's official documentation

Type Documentation

  • Enhanced JSDoc comments with usage examples
  • Clear documentation of both supported formats

API Design

The API provides excellent developer experience with progressive complexity:

// ✅ Simple case - clean and intuitive
propertyOrdering: ['name', 'age', 'email']

// ✅ Complex case - full control over nested structures
propertyOrdering: {
  '': ['name', 'profile'],
  'profile': ['bio', 'settings'],
  'profile.settings': ['theme', 'notifications']
}

Breaking Changes

None. This is a purely additive feature with full backward compatibility.

References


This implementation enables developers to leverage Google's property ordering feature for more consistent, reliable, and high-quality structured outputs from Gemini models, with an API that scales from simple to complex use cases.

Next issue in about 5 hours