Skip to content

Conversation

pnkvalavala
Copy link
Contributor

@pnkvalavala pnkvalavala commented Jul 21, 2025

Description

This PR fixes multiple issues of using function calling and structured outputs with gemini models.
Primarily supporting / fixing enums, nested schemas, null property and completely removing $defs and $ref which were sent as is in most cases

Motivation

Without this, it is not possible to use Gemini's structured data / function calling except with simplest schemas - with absolutely no complexity like:

class SimpleSchema(BaseModel):
    name: str
    age: int

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)

How Has This Been Tested?

  • Unit Tests
  • Integration Tests
  • Manual Testing

Testing code:

Base code:

from enum import StrEnum
from typing import Literal

import instructor
from openai import OpenAI
from portkey_ai import createHeaders
from portkey_ai.api_resources.global_constants import LOCAL_BASE_URL
from pydantic import BaseModel, Field


class StatusEnum(StrEnum):
    ACTIVE = "ACTIVE"
    INACTIVE = "INACTIVE"
    BANNED = "BANNED"


class ContactInfo(BaseModel):
    email: str = Field(..., description="User's email address")
    phone: str | None = Field(None, description="Phone number (E.164 format)")
    address: str = Field(..., description="Address")


class Job(BaseModel):
    title: str
    company: str
    start_date: str | None = None
    end_date: str | None = None
    currently_working: bool = Field(
        ..., description="True if the person is currently employed in this job"
    )


class SocialAccount(BaseModel):
    platform: Literal["twitter", "linkedin", "github", "other"]
    username: str
    url: str | None = None


class Preferences(BaseModel):
    newsletter_subscribed: bool = True
    preferred_languages: list[Literal["en", "es", "fr", "de", "other"]]
    notification_frequency: Literal["daily", "weekly", "monthly"] | None = None


class Pet(BaseModel):
    name: str
    species: Literal["dog", "cat", "bird", "other"]
    age: int | None = None
    microchipped: bool | None = None


class Passport(BaseModel):
    country: str
    number: str
    expiry: str


class NationalID(BaseModel):
    country: str
    id_number: str


class EmergencyContact(BaseModel):
    name: str
    relation: str
    phone: str


class UserProfile(BaseModel):
    id: str = Field(..., description="Unique user ID")
    name: str
    status: StatusEnum
    age: int
    contact: ContactInfo
    jobs: list[Job]
    social: list[SocialAccount] | None = None
    preferences: Preferences
    pets: list[Pet] | None = None
    identity: Passport | NationalID
    emergency_contacts: list[EmergencyContact]
    notes: str | None = None


user_prompt = """\
Extract all the information about this person and structure it using the UserProfile schema.

John Doe is a 32-year-old software engineer (id: u12345). He is currently active.
He can be reached at [email protected] or +12345678901. 
His address is 42 Galaxy Way, Springfield. 
He has worked at Acme Corp (title: Engineer, start: 2016-01, end: 2020-05, not currently working), 
and at FutureTech (title: Senior Engineer, start: 2020-06, currently working). 
He has a cat named Luna (age 2, microchipped). 
John subscribes to newsletters, prefers English and French, and gets notifications weekly.
He has a Twitter account (@johnny) and a LinkedIn (john-doe).
John holds a US passport, number 123456789, expiring 2030-12-31.
In case of emergency, contact Jane Doe (wife) at +10987654321.
"""

Using Function calling:

client = instructor.from_openai(
    OpenAI(
        api_key="GEMINI_API_KEY",
        base_url=LOCAL_BASE_URL,
        default_headers=createHeaders(provider="google"),
    )
)

response = client.chat.completions.create(
    model="gemini-1.5-flash-002",
    response_model=UserProfile,
    messages=[
        {
            "role": "user",
            "content": user_prompt,
        }
    ],
)
print(response.model_dump_json(indent=4))

Current main branch:

Request sent from Portkey, wrapped up as request is too long (click to expand)
{
  "model": "gemini-1.5-flash-002",
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "Extract all the information about this person and structure it using the UserProfile schema.\n\nJohn Doe is a 32-year-old software engineer (id: u12345). He is currently active.\nHe can be reached at [email protected] or +12345678901. \nHis address is 42 Galaxy Way, Springfield. \nHe has worked at Acme Corp (title: Engineer, start: 2016-01, end: 2020-05, not currently working), \nand at FutureTech (title: Senior Engineer, start: 2020-06, currently working). \nHe has a cat named Luna (age 2, microchipped). \nJohn subscribes to newsletters, prefers English and French, and gets notifications weekly.\nHe has a Twitter account (@johnny) and a LinkedIn (john-doe).\nJohn holds a US passport, number 123456789, expiring 2030-12-31.\nIn case of emergency, contact Jane Doe (wife) at +10987654321.\n"
        }
      ]
    }
  ],
  "tools": [
    {
      "functionDeclarations": [
        {
          "name": "UserProfile",
          "description": "Correctly extracted `UserProfile` with all the required parameters with correct types",
          "parameters": {
            "$defs": {
              "ContactInfo": {
                "properties": {
                  "email": {
                    "description": "User's email address",
                    "title": "Email",
                    "type": "string"
                  },
                  "phone": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Phone number (E.164 format)",
                    "title": "Phone"
                  },
                  "address": {
                    "description": "Address",
                    "title": "Address",
                    "type": "string"
                  }
                },
                "required": [
                  "email",
                  "address"
                ],
                "title": "ContactInfo",
                "type": "object"
              },
              "EmergencyContact": {
                "properties": {
                  "name": {
                    "title": "Name",
                    "type": "string"
                  },
                  "relation": {
                    "title": "Relation",
                    "type": "string"
                  },
                  "phone": {
                    "title": "Phone",
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "relation",
                  "phone"
                ],
                "title": "EmergencyContact",
                "type": "object"
              },
              "Job": {
                "properties": {
                  "title": {
                    "title": "Title",
                    "type": "string"
                  },
                  "company": {
                    "title": "Company",
                    "type": "string"
                  },
                  "start_date": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "title": "Start Date"
                  },
                  "end_date": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "title": "End Date"
                  },
                  "currently_working": {
                    "description": "True if the person is currently employed in this job",
                    "title": "Currently Working",
                    "type": "boolean"
                  }
                },
                "required": [
                  "title",
                  "company",
                  "currently_working"
                ],
                "title": "Job",
                "type": "object"
              },
              "NationalID": {
                "properties": {
                  "country": {
                    "title": "Country",
                    "type": "string"
                  },
                  "id_number": {
                    "title": "Id Number",
                    "type": "string"
                  }
                },
                "required": [
                  "country",
                  "id_number"
                ],
                "title": "NationalID",
                "type": "object"
              },
              "Passport": {
                "properties": {
                  "country": {
                    "title": "Country",
                    "type": "string"
                  },
                  "number": {
                    "title": "Number",
                    "type": "string"
                  },
                  "expiry": {
                    "title": "Expiry",
                    "type": "string"
                  }
                },
                "required": [
                  "country",
                  "number",
                  "expiry"
                ],
                "title": "Passport",
                "type": "object"
              },
              "Pet": {
                "properties": {
                  "name": {
                    "title": "Name",
                    "type": "string"
                  },
                  "species": {
                    "enum": [
                      "dog",
                      "cat",
                      "bird",
                      "other"
                    ],
                    "title": "Species",
                    "type": "string"
                  },
                  "age": {
                    "anyOf": [
                      {
                        "type": "integer"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "title": "Age"
                  },
                  "microchipped": {
                    "anyOf": [
                      {
                        "type": "boolean"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "title": "Microchipped"
                  }
                },
                "required": [
                  "name",
                  "species"
                ],
                "title": "Pet",
                "type": "object"
              },
              "Preferences": {
                "properties": {
                  "newsletter_subscribed": {
                    "default": true,
                    "title": "Newsletter Subscribed",
                    "type": "boolean"
                  },
                  "preferred_languages": {
                    "items": {
                      "enum": [
                        "en",
                        "es",
                        "fr",
                        "de",
                        "other"
                      ],
                      "type": "string"
                    },
                    "title": "Preferred Languages",
                    "type": "array"
                  },
                  "notification_frequency": {
                    "anyOf": [
                      {
                        "enum": [
                          "daily",
                          "weekly",
                          "monthly"
                        ],
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "title": "Notification Frequency"
                  }
                },
                "required": [
                  "preferred_languages"
                ],
                "title": "Preferences",
                "type": "object"
              },
              "SocialAccount": {
                "properties": {
                  "platform": {
                    "enum": [
                      "twitter",
                      "linkedin",
                      "github",
                      "other"
                    ],
                    "title": "Platform",
                    "type": "string"
                  },
                  "username": {
                    "title": "Username",
                    "type": "string"
                  },
                  "url": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "title": "Url"
                  }
                },
                "required": [
                  "platform",
                  "username"
                ],
                "title": "SocialAccount",
                "type": "object"
              },
              "StatusEnum": {
                "enum": [
                  "ACTIVE",
                  "INACTIVE",
                  "BANNED"
                ],
                "title": "StatusEnum",
                "type": "string"
              }
            },
            "properties": {
              "id": {
                "description": "Unique user ID",
                "title": "Id",
                "type": "string"
              },
              "name": {
                "title": "Name",
                "type": "string"
              },
              "status": {
                "$ref": "#/$defs/StatusEnum"
              },
              "age": {
                "title": "Age",
                "type": "integer"
              },
              "contact": {
                "$ref": "#/$defs/ContactInfo"
              },
              "jobs": {
                "items": {
                  "$ref": "#/$defs/Job"
                },
                "title": "Jobs",
                "type": "array"
              },
              "social": {
                "anyOf": [
                  {
                    "items": {
                      "$ref": "#/$defs/SocialAccount"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Social"
              },
              "preferences": {
                "$ref": "#/$defs/Preferences"
              },
              "pets": {
                "anyOf": [
                  {
                    "items": {
                      "$ref": "#/$defs/Pet"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Pets"
              },
              "identity": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Passport"
                  },
                  {
                    "$ref": "#/$defs/NationalID"
                  }
                ],
                "title": "Identity"
              },
              "emergency_contacts": {
                "items": {
                  "$ref": "#/$defs/EmergencyContact"
                },
                "title": "Emergency Contacts",
                "type": "array"
              },
              "notes": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Notes"
              }
            },
            "required": [
              "age",
              "contact",
              "emergency_contacts",
              "id",
              "identity",
              "jobs",
              "name",
              "preferences",
              "status"
            ],
            "type": "object"
          }
        }
      ]
    }
  ],
  "tool_config": {
    "function_calling_config": {
      "mode": "ANY",
      "allowed_function_names": [
        "UserProfile"
      ]
    }
  }
}
{
  "error": {
    "message": "google error: Invalid JSON payload received. Unknown name \"$defs\" at 'tools[0].function_declarations[0].parameters': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'tools[0].function_declarations[0].parameters.properties[2].value': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'tools[0].function_declarations[0].parameters.properties[4].value': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'tools[0].function_declarations[0].parameters.properties[5].value.items': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'tools[0].function_declarations[0].parameters.properties[6].value.any_of[0].items': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'tools[0].function_declarations[0].parameters.properties[7].value': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'tools[0].function_declarations[0].parameters.properties[8].value.any_of[0].items': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'tools[0].function_declarations[0].parameters.properties[9].value.any_of[0]': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'tools[0].function_declarations[0].parameters.properties[9].value.any_of[1]': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'tools[0].function_declarations[0].parameters.properties[10].value.items': Cannot find field.",
    "type": "INVALID_ARGUMENT",
    "param": null,
    "code": 400
  },
  "provider": "google"
}

fix/response-schemas-for-gemini-models branch:

Request sent from Portkey (click to expand)
{
  "model": "gemini-1.5-flash-002",
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "Extract all the information about this person and structure it using the UserProfile schema.\n\nJohn Doe is a 32-year-old software engineer (id: u12345). He is currently active.\nHe can be reached at [email protected] or +12345678901. \nHis address is 42 Galaxy Way, Springfield. \nHe has worked at Acme Corp (title: Engineer, start: 2016-01, end: 2020-05, not currently working), \nand at FutureTech (title: Senior Engineer, start: 2020-06, currently working). \nHe has a cat named Luna (age 2, microchipped). \nJohn subscribes to newsletters, prefers English and French, and gets notifications weekly.\nHe has a Twitter account (@johnny) and a LinkedIn (john-doe).\nJohn holds a US passport, number 123456789, expiring 2030-12-31.\nIn case of emergency, contact Jane Doe (wife) at +10987654321.\n"
        }
      ]
    }
  ],
  "tools": [
    {
      "functionDeclarations": [
        {
          "name": "UserProfile",
          "description": "Correctly extracted `UserProfile` with all the required parameters with correct types",
          "parameters": {
            "properties": {
              "id": {
                "description": "Unique user ID",
                "title": "Id",
                "type": "string"
              },
              "name": {
                "title": "Name",
                "type": "string"
              },
              "status": {
                "enum": [
                  "ACTIVE",
                  "INACTIVE",
                  "BANNED"
                ],
                "format": "enum",
                "title": "StatusEnum",
                "type": "string"
              },
              "age": {
                "title": "Age",
                "type": "integer"
              },
              "contact": {
                "properties": {
                  "email": {
                    "description": "User's email address",
                    "title": "Email",
                    "type": "string"
                  },
                  "phone": {
                    "type": "string",
                    "nullable": true,
                    "default": null,
                    "description": "Phone number (E.164 format)",
                    "title": "Phone"
                  },
                  "address": {
                    "description": "Address",
                    "title": "Address",
                    "type": "string"
                  }
                },
                "required": [
                  "email",
                  "address"
                ],
                "title": "ContactInfo",
                "type": "object"
              },
              "jobs": {
                "items": {
                  "properties": {
                    "title": {
                      "title": "Title",
                      "type": "string"
                    },
                    "company": {
                      "title": "Company",
                      "type": "string"
                    },
                    "start_date": {
                      "type": "string",
                      "nullable": true,
                      "default": null,
                      "title": "Start Date"
                    },
                    "end_date": {
                      "type": "string",
                      "nullable": true,
                      "default": null,
                      "title": "End Date"
                    },
                    "currently_working": {
                      "description": "True if the person is currently employed in this job",
                      "title": "Currently Working",
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "title",
                    "company",
                    "currently_working"
                  ],
                  "title": "Job",
                  "type": "object"
                },
                "title": "Jobs",
                "type": "array"
              },
              "social": {
                "items": {
                  "properties": {
                    "platform": {
                      "enum": [
                        "twitter",
                        "linkedin",
                        "github",
                        "other"
                      ],
                      "format": "enum",
                      "title": "Platform",
                      "type": "string"
                    },
                    "username": {
                      "title": "Username",
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "nullable": true,
                      "default": null,
                      "title": "Url"
                    }
                  },
                  "required": [
                    "platform",
                    "username"
                  ],
                  "title": "SocialAccount",
                  "type": "object"
                },
                "type": "array",
                "nullable": true,
                "default": null,
                "title": "Social"
              },
              "preferences": {
                "properties": {
                  "newsletter_subscribed": {
                    "default": true,
                    "title": "Newsletter Subscribed",
                    "type": "boolean"
                  },
                  "preferred_languages": {
                    "items": {
                      "enum": [
                        "en",
                        "es",
                        "fr",
                        "de",
                        "other"
                      ],
                      "format": "enum",
                      "type": "string"
                    },
                    "title": "Preferred Languages",
                    "type": "array"
                  },
                  "notification_frequency": {
                    "enum": [
                      "daily",
                      "weekly",
                      "monthly"
                    ],
                    "format": "enum",
                    "type": "string",
                    "nullable": true,
                    "default": null,
                    "title": "Notification Frequency"
                  }
                },
                "required": [
                  "preferred_languages"
                ],
                "title": "Preferences",
                "type": "object"
              },
              "pets": {
                "items": {
                  "properties": {
                    "name": {
                      "title": "Name",
                      "type": "string"
                    },
                    "species": {
                      "enum": [
                        "dog",
                        "cat",
                        "bird",
                        "other"
                      ],
                      "format": "enum",
                      "title": "Species",
                      "type": "string"
                    },
                    "age": {
                      "type": "integer",
                      "nullable": true,
                      "default": null,
                      "title": "Age"
                    },
                    "microchipped": {
                      "type": "boolean",
                      "nullable": true,
                      "default": null,
                      "title": "Microchipped"
                    }
                  },
                  "required": [
                    "name",
                    "species"
                  ],
                  "title": "Pet",
                  "type": "object"
                },
                "type": "array",
                "nullable": true,
                "default": null,
                "title": "Pets"
              },
              "identity": {
                "anyOf": [
                  {
                    "properties": {
                      "country": {
                        "title": "Country",
                        "type": "string"
                      },
                      "number": {
                        "title": "Number",
                        "type": "string"
                      },
                      "expiry": {
                        "title": "Expiry",
                        "type": "string"
                      }
                    },
                    "required": [
                      "country",
                      "number",
                      "expiry"
                    ],
                    "title": "Passport",
                    "type": "object"
                  },
                  {
                    "properties": {
                      "country": {
                        "title": "Country",
                        "type": "string"
                      },
                      "id_number": {
                        "title": "Id Number",
                        "type": "string"
                      }
                    },
                    "required": [
                      "country",
                      "id_number"
                    ],
                    "title": "NationalID",
                    "type": "object"
                  }
                ],
                "title": "Identity"
              },
              "emergency_contacts": {
                "items": {
                  "properties": {
                    "name": {
                      "title": "Name",
                      "type": "string"
                    },
                    "relation": {
                      "title": "Relation",
                      "type": "string"
                    },
                    "phone": {
                      "title": "Phone",
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "relation",
                    "phone"
                  ],
                  "title": "EmergencyContact",
                  "type": "object"
                },
                "title": "Emergency Contacts",
                "type": "array"
              },
              "notes": {
                "type": "string",
                "nullable": true,
                "default": null,
                "title": "Notes"
              }
            },
            "required": [
              "age",
              "contact",
              "emergency_contacts",
              "id",
              "identity",
              "jobs",
              "name",
              "preferences",
              "status"
            ],
            "type": "object"
          }
        }
      ]
    }
  ],
  "tool_config": {
    "function_calling_config": {
      "mode": "ANY",
      "allowed_function_names": [
        "UserProfile"
      ]
    }
  }
}

Response:

{
  "id": "portkey-437855d5-84fe-4fc2-befc-bd82e1416e9c",
  "object": "chat.completion",
  "created": 1753634510,
  "model": "gemini-1.5-flash-002",
  "provider": "google",
  "choices": [
    {
      "message": {
        "role": "assistant",
        "tool_calls": [
          {
            "id": "portkey-e037b126-7668-4ca0-ace1-f79280201f32",
            "type": "function",
            "function": {
              "name": "UserProfile",
              "arguments": "{\"pets\":[{\"microchipped\":true,\"age\":2,\"species\":\"cat\",\"name\":\"Luna\"}],\"age\":32,\"preferences\":{\"preferred_languages\":[\"en\",\"fr\"],\"newsletter_subscribed\":true,\"notification_frequency\":\"weekly\"},\"social\":[{\"username\":\"johnny\",\"platform\":\"twitter\"},{\"platform\":\"linkedin\",\"username\":\"john-doe\"}],\"status\":\"ACTIVE\",\"name\":\"John Doe\",\"emergency_contacts\":[{\"phone\":\"+10987654321\",\"name\":\"Jane Doe\",\"relation\":\"wife\"}],\"contact\":{\"phone\":\"+12345678901\",\"address\":\"42 Galaxy Way, Springfield\",\"email\":\"[email protected]\"},\"id\":\"u12345\",\"jobs\":[{\"start_date\":\"2016-01\",\"currently_working\":false,\"end_date\":\"2020-05\",\"title\":\"Engineer\",\"company\":\"Acme Corp\"},{\"title\":\"Senior Engineer\",\"company\":\"FutureTech\",\"currently_working\":true,\"start_date\":\"2020-06\"}],\"identity\":{\"country\":\"US\",\"number\":\"123456789\",\"expiry\":\"2030-12-31\"}}"
            }
          }
        ]
      },
      "index": 0,
      "finish_reason": "STOP"
    }
  ],
  "usage": {
    "prompt_tokens": 487,
    "completion_tokens": 176,
    "total_tokens": 663,
    "completion_tokens_details": {
      "reasoning_tokens": 0
    }
  }
}

Using Gemini Structured outputs (JSON Schema) - Ignore system prompt being added, that is from instructor library being added in JSON_SCHEMA mode

client = instructor.from_openai(
    OpenAI(
        api_key="GEMINI_API_KEY",
        base_url=LOCAL_BASE_URL,
        default_headers=createHeaders(provider="google"),
    ),
    mode=instructor.Mode.JSON_SCHEMA,
)

response = client.chat.completions.create(
    model="gemini-1.5-flash-002",
    response_model=UserProfile,
    messages=[
        {
            "role": "user",
            "content": user_prompt,
        }
    ],
)
print(response.model_dump_json(indent=4))

Current main branch:

Request sent from Portkey (click to expand)
{
  "model": "gemini-1.5-flash-002",
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "Extract all the information about this person and structure it using the UserProfile schema.\n\nJohn Doe is a 32-year-old software engineer (id: u12345). He is currently active.\nHe can be reached at [email protected] or +12345678901. \nHis address is 42 Galaxy Way, Springfield. \nHe has worked at Acme Corp (title: Engineer, start: 2016-01, end: 2020-05, not currently working), \nand at FutureTech (title: Senior Engineer, start: 2020-06, currently working). \nHe has a cat named Luna (age 2, microchipped). \nJohn subscribes to newsletters, prefers English and French, and gets notifications weekly.\nHe has a Twitter account (@johnny) and a LinkedIn (john-doe).\nJohn holds a US passport, number 123456789, expiring 2030-12-31.\nIn case of emergency, contact Jane Doe (wife) at +10987654321.\n"
        }
      ]
    }
  ],
  "systemInstruction": {
    "parts": [
      {
        "text": "\n        As a genius expert, your task is to understand the content and provide\n        the parsed objects in json that match the following json_schema:\n\n\n        {\n  \"$defs\": {\n    \"ContactInfo\": {\n      \"properties\": {\n        \"email\": {\n          \"description\": \"User's email address\",\n          \"title\": \"Email\",\n          \"type\": \"string\"\n        },\n        \"phone\": {\n          \"anyOf\": [\n            {\n              \"type\": \"string\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"description\": \"Phone number (E.164 format)\",\n          \"title\": \"Phone\"\n        },\n        \"address\": {\n          \"description\": \"Address\",\n          \"title\": \"Address\",\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"address\"\n      ],\n      \"title\": \"ContactInfo\",\n      \"type\": \"object\"\n    },\n    \"EmergencyContact\": {\n      \"properties\": {\n        \"name\": {\n          \"title\": \"Name\",\n          \"type\": \"string\"\n        },\n        \"relation\": {\n          \"title\": \"Relation\",\n          \"type\": \"string\"\n        },\n        \"phone\": {\n          \"title\": \"Phone\",\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"name\",\n        \"relation\",\n        \"phone\"\n      ],\n      \"title\": \"EmergencyContact\",\n      \"type\": \"object\"\n    },\n    \"Job\": {\n      \"properties\": {\n        \"title\": {\n          \"title\": \"Title\",\n          \"type\": \"string\"\n        },\n        \"company\": {\n          \"title\": \"Company\",\n          \"type\": \"string\"\n        },\n        \"start_date\": {\n          \"anyOf\": [\n            {\n              \"type\": \"string\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"Start Date\"\n        },\n        \"end_date\": {\n          \"anyOf\": [\n            {\n              \"type\": \"string\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"End Date\"\n        },\n        \"currently_working\": {\n          \"description\": \"True if the person is currently employed in this job\",\n          \"title\": \"Currently Working\",\n          \"type\": \"boolean\"\n        }\n      },\n      \"required\": [\n        \"title\",\n        \"company\",\n        \"currently_working\"\n      ],\n      \"title\": \"Job\",\n      \"type\": \"object\"\n    },\n    \"NationalID\": {\n      \"properties\": {\n        \"country\": {\n          \"title\": \"Country\",\n          \"type\": \"string\"\n        },\n        \"id_number\": {\n          \"title\": \"Id Number\",\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"country\",\n        \"id_number\"\n      ],\n      \"title\": \"NationalID\",\n      \"type\": \"object\"\n    },\n    \"Passport\": {\n      \"properties\": {\n        \"country\": {\n          \"title\": \"Country\",\n          \"type\": \"string\"\n        },\n        \"number\": {\n          \"title\": \"Number\",\n          \"type\": \"string\"\n        },\n        \"expiry\": {\n          \"title\": \"Expiry\",\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"country\",\n        \"number\",\n        \"expiry\"\n      ],\n      \"title\": \"Passport\",\n      \"type\": \"object\"\n    },\n    \"Pet\": {\n      \"properties\": {\n        \"name\": {\n          \"title\": \"Name\",\n          \"type\": \"string\"\n        },\n        \"species\": {\n          \"enum\": [\n            \"dog\",\n            \"cat\",\n            \"bird\",\n            \"other\"\n          ],\n          \"title\": \"Species\",\n          \"type\": \"string\"\n        },\n        \"age\": {\n          \"anyOf\": [\n            {\n              \"type\": \"integer\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"Age\"\n        },\n        \"microchipped\": {\n          \"anyOf\": [\n            {\n              \"type\": \"boolean\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"Microchipped\"\n        }\n      },\n      \"required\": [\n        \"name\",\n        \"species\"\n      ],\n      \"title\": \"Pet\",\n      \"type\": \"object\"\n    },\n    \"Preferences\": {\n      \"properties\": {\n        \"newsletter_subscribed\": {\n          \"default\": true,\n          \"title\": \"Newsletter Subscribed\",\n          \"type\": \"boolean\"\n        },\n        \"preferred_languages\": {\n          \"items\": {\n            \"enum\": [\n              \"en\",\n              \"es\",\n              \"fr\",\n              \"de\",\n              \"other\"\n            ],\n            \"type\": \"string\"\n          },\n          \"title\": \"Preferred Languages\",\n          \"type\": \"array\"\n        },\n        \"notification_frequency\": {\n          \"anyOf\": [\n            {\n              \"enum\": [\n                \"daily\",\n                \"weekly\",\n                \"monthly\"\n              ],\n              \"type\": \"string\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"Notification Frequency\"\n        }\n      },\n      \"required\": [\n        \"preferred_languages\"\n      ],\n      \"title\": \"Preferences\",\n      \"type\": \"object\"\n    },\n    \"SocialAccount\": {\n      \"properties\": {\n        \"platform\": {\n          \"enum\": [\n            \"twitter\",\n            \"linkedin\",\n            \"github\",\n            \"other\"\n          ],\n          \"title\": \"Platform\",\n          \"type\": \"string\"\n        },\n        \"username\": {\n          \"title\": \"Username\",\n          \"type\": \"string\"\n        },\n        \"url\": {\n          \"anyOf\": [\n            {\n              \"type\": \"string\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"Url\"\n        }\n      },\n      \"required\": [\n        \"platform\",\n        \"username\"\n      ],\n      \"title\": \"SocialAccount\",\n      \"type\": \"object\"\n    },\n    \"StatusEnum\": {\n      \"enum\": [\n        \"ACTIVE\",\n        \"INACTIVE\",\n        \"BANNED\"\n      ],\n      \"title\": \"StatusEnum\",\n      \"type\": \"string\"\n    }\n  },\n  \"properties\": {\n    \"id\": {\n      \"description\": \"Unique user ID\",\n      \"title\": \"Id\",\n      \"type\": \"string\"\n    },\n    \"name\": {\n      \"title\": \"Name\",\n      \"type\": \"string\"\n    },\n    \"status\": {\n      \"$ref\": \"#/$defs/StatusEnum\"\n    },\n    \"age\": {\n      \"title\": \"Age\",\n      \"type\": \"integer\"\n    },\n    \"contact\": {\n      \"$ref\": \"#/$defs/ContactInfo\"\n    },\n    \"jobs\": {\n      \"items\": {\n        \"$ref\": \"#/$defs/Job\"\n      },\n      \"title\": \"Jobs\",\n      \"type\": \"array\"\n    },\n    \"social\": {\n      \"anyOf\": [\n        {\n          \"items\": {\n            \"$ref\": \"#/$defs/SocialAccount\"\n          },\n          \"type\": \"array\"\n        },\n        {\n          \"type\": \"null\"\n        }\n      ],\n      \"default\": null,\n      \"title\": \"Social\"\n    },\n    \"preferences\": {\n      \"$ref\": \"#/$defs/Preferences\"\n    },\n    \"pets\": {\n      \"anyOf\": [\n        {\n          \"items\": {\n            \"$ref\": \"#/$defs/Pet\"\n          },\n          \"type\": \"array\"\n        },\n        {\n          \"type\": \"null\"\n        }\n      ],\n      \"default\": null,\n      \"title\": \"Pets\"\n    },\n    \"identity\": {\n      \"anyOf\": [\n        {\n          \"$ref\": \"#/$defs/Passport\"\n        },\n        {\n          \"$ref\": \"#/$defs/NationalID\"\n        }\n      ],\n      \"title\": \"Identity\"\n    },\n    \"emergency_contacts\": {\n      \"items\": {\n        \"$ref\": \"#/$defs/EmergencyContact\"\n      },\n      \"title\": \"Emergency Contacts\",\n      \"type\": \"array\"\n    },\n    \"notes\": {\n      \"anyOf\": [\n        {\n          \"type\": \"string\"\n        },\n        {\n          \"type\": \"null\"\n        }\n      ],\n      \"default\": null,\n      \"title\": \"Notes\"\n    }\n  },\n  \"required\": [\n    \"id\",\n    \"name\",\n    \"status\",\n    \"age\",\n    \"contact\",\n    \"jobs\",\n    \"preferences\",\n    \"identity\",\n    \"emergency_contacts\"\n  ],\n  \"title\": \"UserProfile\",\n  \"type\": \"object\"\n}\n\n        Make sure to return an instance of the JSON, not the schema itself\n"
      }
    ],
    "role": "system"
  },
  "generationConfig": {
    "responseMimeType": "application/json",
    "responseSchema": {
      "properties": {
        "id": {
          "description": "Unique user ID",
          "title": "Id",
          "type": "string"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "status": {
          "enum": [
            "ACTIVE",
            "INACTIVE",
            "BANNED"
          ],
          "title": "StatusEnum",
          "type": "string"
        },
        "age": {
          "title": "Age",
          "type": "integer"
        },
        "contact": {
          "properties": {
            "email": {
              "description": "User's email address",
              "title": "Email",
              "type": "string"
            },
            "phone": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "default": {},
              "description": "Phone number (E.164 format)",
              "title": "Phone"
            },
            "address": {
              "description": "Address",
              "title": "Address",
              "type": "string"
            }
          },
          "required": [
            "email",
            "address"
          ],
          "title": "ContactInfo",
          "type": "object"
        },
        "jobs": {
          "items": {
            "properties": {
              "title": {
                "title": "Title",
                "type": "string"
              },
              "company": {
                "title": "Company",
                "type": "string"
              },
              "start_date": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": {},
                "title": "Start Date"
              },
              "end_date": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": {},
                "title": "End Date"
              },
              "currently_working": {
                "description": "True if the person is currently employed in this job",
                "title": "Currently Working",
                "type": "boolean"
              }
            },
            "required": [
              "title",
              "company",
              "currently_working"
            ],
            "title": "Job",
            "type": "object"
          },
          "title": "Jobs",
          "type": "array"
        },
        "social": {
          "anyOf": [
            {
              "items": {
                "$ref": "#/$defs/SocialAccount"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": {},
          "title": "Social"
        },
        "preferences": {
          "properties": {
            "newsletter_subscribed": {
              "default": {},
              "title": "Newsletter Subscribed",
              "type": "boolean"
            },
            "preferred_languages": {
              "items": {
                "enum": [
                  "en",
                  "es",
                  "fr",
                  "de",
                  "other"
                ],
                "type": "string"
              },
              "title": "Preferred Languages",
              "type": "array"
            },
            "notification_frequency": {
              "anyOf": [
                {
                  "enum": [
                    "daily",
                    "weekly",
                    "monthly"
                  ],
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "default": {},
              "title": "Notification Frequency"
            }
          },
          "required": [
            "preferred_languages"
          ],
          "title": "Preferences",
          "type": "object"
        },
        "pets": {
          "anyOf": [
            {
              "items": {
                "$ref": "#/$defs/Pet"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": {},
          "title": "Pets"
        },
        "identity": {
          "anyOf": [
            {
              "$ref": "#/$defs/Passport"
            },
            {
              "$ref": "#/$defs/NationalID"
            }
          ],
          "title": "Identity"
        },
        "emergency_contacts": {
          "items": {
            "properties": {
              "name": {
                "title": "Name",
                "type": "string"
              },
              "relation": {
                "title": "Relation",
                "type": "string"
              },
              "phone": {
                "title": "Phone",
                "type": "string"
              }
            },
            "required": [
              "name",
              "relation",
              "phone"
            ],
            "title": "EmergencyContact",
            "type": "object"
          },
          "title": "Emergency Contacts",
          "type": "array"
        },
        "notes": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": {},
          "title": "Notes"
        }
      },
      "required": [
        "id",
        "name",
        "status",
        "age",
        "contact",
        "jobs",
        "preferences",
        "identity",
        "emergency_contacts"
      ],
      "title": "UserProfile",
      "type": "object"
    }
  }
}

Response:

{
  "error": {
    "message": "google error: Invalid JSON payload received. Unknown name \"$ref\" at 'generation_config.response_schema.properties[6].value.any_of[0].items': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'generation_config.response_schema.properties[8].value.any_of[0].items': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'generation_config.response_schema.properties[9].value.any_of[0]': Cannot find field.\nInvalid JSON payload received. Unknown name \"$ref\" at 'generation_config.response_schema.properties[9].value.any_of[1]': Cannot find field.",
    "type": "INVALID_ARGUMENT",
    "param": null,
    "code": 400
  },
  "provider": "google"
}

fix/response-schemas-for-gemini-models branch:

Request sent from Portkey (click to expand)
{
  "model": "gemini-1.5-flash-002",
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "Extract all the information about this person and structure it using the UserProfile schema.\n\nJohn Doe is a 32-year-old software engineer (id: u12345). He is currently active.\nHe can be reached at [email protected] or +12345678901. \nHis address is 42 Galaxy Way, Springfield. \nHe has worked at Acme Corp (title: Engineer, start: 2016-01, end: 2020-05, not currently working), \nand at FutureTech (title: Senior Engineer, start: 2020-06, currently working). \nHe has a cat named Luna (age 2, microchipped). \nJohn subscribes to newsletters, prefers English and French, and gets notifications weekly.\nHe has a Twitter account (@johnny) and a LinkedIn (john-doe).\nJohn holds a US passport, number 123456789, expiring 2030-12-31.\nIn case of emergency, contact Jane Doe (wife) at +10987654321.\n"
        }
      ]
    }
  ],
  "systemInstruction": {
    "parts": [
      {
        "text": "\n        As a genius expert, your task is to understand the content and provide\n        the parsed objects in json that match the following json_schema:\n\n\n        {\n  \"$defs\": {\n    \"ContactInfo\": {\n      \"properties\": {\n        \"email\": {\n          \"description\": \"User's email address\",\n          \"title\": \"Email\",\n          \"type\": \"string\"\n        },\n        \"phone\": {\n          \"anyOf\": [\n            {\n              \"type\": \"string\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"description\": \"Phone number (E.164 format)\",\n          \"title\": \"Phone\"\n        },\n        \"address\": {\n          \"description\": \"Address\",\n          \"title\": \"Address\",\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"address\"\n      ],\n      \"title\": \"ContactInfo\",\n      \"type\": \"object\"\n    },\n    \"EmergencyContact\": {\n      \"properties\": {\n        \"name\": {\n          \"title\": \"Name\",\n          \"type\": \"string\"\n        },\n        \"relation\": {\n          \"title\": \"Relation\",\n          \"type\": \"string\"\n        },\n        \"phone\": {\n          \"title\": \"Phone\",\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"name\",\n        \"relation\",\n        \"phone\"\n      ],\n      \"title\": \"EmergencyContact\",\n      \"type\": \"object\"\n    },\n    \"Job\": {\n      \"properties\": {\n        \"title\": {\n          \"title\": \"Title\",\n          \"type\": \"string\"\n        },\n        \"company\": {\n          \"title\": \"Company\",\n          \"type\": \"string\"\n        },\n        \"start_date\": {\n          \"anyOf\": [\n            {\n              \"type\": \"string\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"Start Date\"\n        },\n        \"end_date\": {\n          \"anyOf\": [\n            {\n              \"type\": \"string\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"End Date\"\n        },\n        \"currently_working\": {\n          \"description\": \"True if the person is currently employed in this job\",\n          \"title\": \"Currently Working\",\n          \"type\": \"boolean\"\n        }\n      },\n      \"required\": [\n        \"title\",\n        \"company\",\n        \"currently_working\"\n      ],\n      \"title\": \"Job\",\n      \"type\": \"object\"\n    },\n    \"NationalID\": {\n      \"properties\": {\n        \"country\": {\n          \"title\": \"Country\",\n          \"type\": \"string\"\n        },\n        \"id_number\": {\n          \"title\": \"Id Number\",\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"country\",\n        \"id_number\"\n      ],\n      \"title\": \"NationalID\",\n      \"type\": \"object\"\n    },\n    \"Passport\": {\n      \"properties\": {\n        \"country\": {\n          \"title\": \"Country\",\n          \"type\": \"string\"\n        },\n        \"number\": {\n          \"title\": \"Number\",\n          \"type\": \"string\"\n        },\n        \"expiry\": {\n          \"title\": \"Expiry\",\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"country\",\n        \"number\",\n        \"expiry\"\n      ],\n      \"title\": \"Passport\",\n      \"type\": \"object\"\n    },\n    \"Pet\": {\n      \"properties\": {\n        \"name\": {\n          \"title\": \"Name\",\n          \"type\": \"string\"\n        },\n        \"species\": {\n          \"enum\": [\n            \"dog\",\n            \"cat\",\n            \"bird\",\n            \"other\"\n          ],\n          \"title\": \"Species\",\n          \"type\": \"string\"\n        },\n        \"age\": {\n          \"anyOf\": [\n            {\n              \"type\": \"integer\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"Age\"\n        },\n        \"microchipped\": {\n          \"anyOf\": [\n            {\n              \"type\": \"boolean\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"Microchipped\"\n        }\n      },\n      \"required\": [\n        \"name\",\n        \"species\"\n      ],\n      \"title\": \"Pet\",\n      \"type\": \"object\"\n    },\n    \"Preferences\": {\n      \"properties\": {\n        \"newsletter_subscribed\": {\n          \"default\": true,\n          \"title\": \"Newsletter Subscribed\",\n          \"type\": \"boolean\"\n        },\n        \"preferred_languages\": {\n          \"items\": {\n            \"enum\": [\n              \"en\",\n              \"es\",\n              \"fr\",\n              \"de\",\n              \"other\"\n            ],\n            \"type\": \"string\"\n          },\n          \"title\": \"Preferred Languages\",\n          \"type\": \"array\"\n        },\n        \"notification_frequency\": {\n          \"anyOf\": [\n            {\n              \"enum\": [\n                \"daily\",\n                \"weekly\",\n                \"monthly\"\n              ],\n              \"type\": \"string\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"Notification Frequency\"\n        }\n      },\n      \"required\": [\n        \"preferred_languages\"\n      ],\n      \"title\": \"Preferences\",\n      \"type\": \"object\"\n    },\n    \"SocialAccount\": {\n      \"properties\": {\n        \"platform\": {\n          \"enum\": [\n            \"twitter\",\n            \"linkedin\",\n            \"github\",\n            \"other\"\n          ],\n          \"title\": \"Platform\",\n          \"type\": \"string\"\n        },\n        \"username\": {\n          \"title\": \"Username\",\n          \"type\": \"string\"\n        },\n        \"url\": {\n          \"anyOf\": [\n            {\n              \"type\": \"string\"\n            },\n            {\n              \"type\": \"null\"\n            }\n          ],\n          \"default\": null,\n          \"title\": \"Url\"\n        }\n      },\n      \"required\": [\n        \"platform\",\n        \"username\"\n      ],\n      \"title\": \"SocialAccount\",\n      \"type\": \"object\"\n    },\n    \"StatusEnum\": {\n      \"enum\": [\n        \"ACTIVE\",\n        \"INACTIVE\",\n        \"BANNED\"\n      ],\n      \"title\": \"StatusEnum\",\n      \"type\": \"string\"\n    }\n  },\n  \"properties\": {\n    \"id\": {\n      \"description\": \"Unique user ID\",\n      \"title\": \"Id\",\n      \"type\": \"string\"\n    },\n    \"name\": {\n      \"title\": \"Name\",\n      \"type\": \"string\"\n    },\n    \"status\": {\n      \"$ref\": \"#/$defs/StatusEnum\"\n    },\n    \"age\": {\n      \"title\": \"Age\",\n      \"type\": \"integer\"\n    },\n    \"contact\": {\n      \"$ref\": \"#/$defs/ContactInfo\"\n    },\n    \"jobs\": {\n      \"items\": {\n        \"$ref\": \"#/$defs/Job\"\n      },\n      \"title\": \"Jobs\",\n      \"type\": \"array\"\n    },\n    \"social\": {\n      \"anyOf\": [\n        {\n          \"items\": {\n            \"$ref\": \"#/$defs/SocialAccount\"\n          },\n          \"type\": \"array\"\n        },\n        {\n          \"type\": \"null\"\n        }\n      ],\n      \"default\": null,\n      \"title\": \"Social\"\n    },\n    \"preferences\": {\n      \"$ref\": \"#/$defs/Preferences\"\n    },\n    \"pets\": {\n      \"anyOf\": [\n        {\n          \"items\": {\n            \"$ref\": \"#/$defs/Pet\"\n          },\n          \"type\": \"array\"\n        },\n        {\n          \"type\": \"null\"\n        }\n      ],\n      \"default\": null,\n      \"title\": \"Pets\"\n    },\n    \"identity\": {\n      \"anyOf\": [\n        {\n          \"$ref\": \"#/$defs/Passport\"\n        },\n        {\n          \"$ref\": \"#/$defs/NationalID\"\n        }\n      ],\n      \"title\": \"Identity\"\n    },\n    \"emergency_contacts\": {\n      \"items\": {\n        \"$ref\": \"#/$defs/EmergencyContact\"\n      },\n      \"title\": \"Emergency Contacts\",\n      \"type\": \"array\"\n    },\n    \"notes\": {\n      \"anyOf\": [\n        {\n          \"type\": \"string\"\n        },\n        {\n          \"type\": \"null\"\n        }\n      ],\n      \"default\": null,\n      \"title\": \"Notes\"\n    }\n  },\n  \"required\": [\n    \"id\",\n    \"name\",\n    \"status\",\n    \"age\",\n    \"contact\",\n    \"jobs\",\n    \"preferences\",\n    \"identity\",\n    \"emergency_contacts\"\n  ],\n  \"title\": \"UserProfile\",\n  \"type\": \"object\"\n}\n\n        Make sure to return an instance of the JSON, not the schema itself\n"
      }
    ],
    "role": "system"
  },
  "generationConfig": {
    "responseMimeType": "application/json",
    "responseSchema": {
      "properties": {
        "id": {
          "description": "Unique user ID",
          "title": "Id",
          "type": "string"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "status": {
          "enum": [
            "ACTIVE",
            "INACTIVE",
            "BANNED"
          ],
          "format": "enum",
          "title": "StatusEnum",
          "type": "string"
        },
        "age": {
          "title": "Age",
          "type": "integer"
        },
        "contact": {
          "properties": {
            "email": {
              "description": "User's email address",
              "title": "Email",
              "type": "string"
            },
            "phone": {
              "type": "string",
              "nullable": true,
              "default": null,
              "description": "Phone number (E.164 format)",
              "title": "Phone"
            },
            "address": {
              "description": "Address",
              "title": "Address",
              "type": "string"
            }
          },
          "required": [
            "email",
            "address"
          ],
          "title": "ContactInfo",
          "type": "object"
        },
        "jobs": {
          "items": {
            "properties": {
              "title": {
                "title": "Title",
                "type": "string"
              },
              "company": {
                "title": "Company",
                "type": "string"
              },
              "start_date": {
                "type": "string",
                "nullable": true,
                "default": null,
                "title": "Start Date"
              },
              "end_date": {
                "type": "string",
                "nullable": true,
                "default": null,
                "title": "End Date"
              },
              "currently_working": {
                "description": "True if the person is currently employed in this job",
                "title": "Currently Working",
                "type": "boolean"
              }
            },
            "required": [
              "title",
              "company",
              "currently_working"
            ],
            "title": "Job",
            "type": "object"
          },
          "title": "Jobs",
          "type": "array"
        },
        "social": {
          "items": {
            "properties": {
              "platform": {
                "enum": [
                  "twitter",
                  "linkedin",
                  "github",
                  "other"
                ],
                "format": "enum",
                "title": "Platform",
                "type": "string"
              },
              "username": {
                "title": "Username",
                "type": "string"
              },
              "url": {
                "type": "string",
                "nullable": true,
                "default": null,
                "title": "Url"
              }
            },
            "required": [
              "platform",
              "username"
            ],
            "title": "SocialAccount",
            "type": "object"
          },
          "type": "array",
          "nullable": true,
          "default": null,
          "title": "Social"
        },
        "preferences": {
          "properties": {
            "newsletter_subscribed": {
              "default": true,
              "title": "Newsletter Subscribed",
              "type": "boolean"
            },
            "preferred_languages": {
              "items": {
                "enum": [
                  "en",
                  "es",
                  "fr",
                  "de",
                  "other"
                ],
                "format": "enum",
                "type": "string"
              },
              "title": "Preferred Languages",
              "type": "array"
            },
            "notification_frequency": {
              "enum": [
                "daily",
                "weekly",
                "monthly"
              ],
              "format": "enum",
              "type": "string",
              "nullable": true,
              "default": null,
              "title": "Notification Frequency"
            }
          },
          "required": [
            "preferred_languages"
          ],
          "title": "Preferences",
          "type": "object"
        },
        "pets": {
          "items": {
            "properties": {
              "name": {
                "title": "Name",
                "type": "string"
              },
              "species": {
                "enum": [
                  "dog",
                  "cat",
                  "bird",
                  "other"
                ],
                "format": "enum",
                "title": "Species",
                "type": "string"
              },
              "age": {
                "type": "integer",
                "nullable": true,
                "default": null,
                "title": "Age"
              },
              "microchipped": {
                "type": "boolean",
                "nullable": true,
                "default": null,
                "title": "Microchipped"
              }
            },
            "required": [
              "name",
              "species"
            ],
            "title": "Pet",
            "type": "object"
          },
          "type": "array",
          "nullable": true,
          "default": null,
          "title": "Pets"
        },
        "identity": {
          "anyOf": [
            {
              "properties": {
                "country": {
                  "title": "Country",
                  "type": "string"
                },
                "number": {
                  "title": "Number",
                  "type": "string"
                },
                "expiry": {
                  "title": "Expiry",
                  "type": "string"
                }
              },
              "required": [
                "country",
                "number",
                "expiry"
              ],
              "title": "Passport",
              "type": "object"
            },
            {
              "properties": {
                "country": {
                  "title": "Country",
                  "type": "string"
                },
                "id_number": {
                  "title": "Id Number",
                  "type": "string"
                }
              },
              "required": [
                "country",
                "id_number"
              ],
              "title": "NationalID",
              "type": "object"
            }
          ],
          "title": "Identity"
        },
        "emergency_contacts": {
          "items": {
            "properties": {
              "name": {
                "title": "Name",
                "type": "string"
              },
              "relation": {
                "title": "Relation",
                "type": "string"
              },
              "phone": {
                "title": "Phone",
                "type": "string"
              }
            },
            "required": [
              "name",
              "relation",
              "phone"
            ],
            "title": "EmergencyContact",
            "type": "object"
          },
          "title": "Emergency Contacts",
          "type": "array"
        },
        "notes": {
          "type": "string",
          "nullable": true,
          "default": null,
          "title": "Notes"
        }
      },
      "required": [
        "id",
        "name",
        "status",
        "age",
        "contact",
        "jobs",
        "preferences",
        "identity",
        "emergency_contacts"
      ],
      "title": "UserProfile",
      "type": "object"
    }
  }
}

Response:

{
  "id": "portkey-4d181706-d546-4f15-a80e-e8375e261282",
  "object": "chat.completion",
  "created": 1753634770,
  "model": "gemini-1.5-flash-002",
  "provider": "google",
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "{\"age\": 32, \"contact\": {\"address\": \"42 Galaxy Way, Springfield\", \"email\": \"[email protected]\", \"phone\": \"+12345678901\"}, \"emergency_contacts\": [{\"name\": \"Jane Doe\", \"phone\": \"+10987654321\", \"relation\": \"wife\"}], \"id\": \"u12345\", \"identity\": {\"country\": \"US\", \"expiry\": \"2030-12-31\", \"number\": \"123456789\"}, \"jobs\": [{\"company\": \"Acme Corp\", \"currently_working\": false, \"title\": \"Engineer\", \"end_date\": \"2020-05\", \"start_date\": \"2016-01\"}, {\"company\": \"FutureTech\", \"currently_working\": true, \"title\": \"Senior Engineer\", \"start_date\": \"2020-06\"}], \"name\": \"John Doe\", \"preferences\": {\"preferred_languages\": [\"en\", \"fr\"], \"newsletter_subscribed\": true, \"notification_frequency\": \"weekly\"}, \"status\": \"ACTIVE\", \"pets\": [{\"name\": \"Luna\", \"species\": \"cat\", \"age\": 2, \"microchipped\": true}], \"social\": [{\"platform\": \"twitter\", \"username\": \"@johnny\"}, {\"platform\": \"linkedin\", \"username\": \"john-doe\"}]}"
      },
      "index": 0,
      "finish_reason": "STOP"
    }
  ],
  "usage": {
    "prompt_tokens": 2831,
    "completion_tokens": 326,
    "total_tokens": 3157,
    "completion_tokens_details": {
      "reasoning_tokens": 0
    }
  }
}

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Related Issues

#837 (comment)
https://discord.com/channels/1143393887742861333/1317117120676364390/1317125306124992523

Copy link
Contributor

matter-code-review bot commented Jul 21, 2025

Code Quality bug fix

Description

Summary By MatterAI MatterAI logo

🔄 What Changed

This PR addresses an issue where nested models weren’t working with Gemini tool parameters due to schemas with $defs and $ref being sent as-is. The derefer function in src/providers/google-vertex-ai/utils.ts was modified. Previously, it returned a new object with resolved and sibling properties. Now, it explicitly clears the original node's properties and then assigns the resolved and siblings properties directly to the node object. This ensures in-place mutation and correct handling of object references during schema dereferencing, allowing schemas to be properly flattened for Gemini.

🔍 Impact of the Change

This change ensures that complex nested schemas, particularly those utilizing $defs and $ref, are correctly processed and flattened before being transmitted to the Gemini API. This resolves a critical bug that prevented proper function calling and structured outputs when dealing with intricate data models, significantly improving the reliability and correctness of the Gemini integration.

📁 Total Files Changed

  • src/providers/google-vertex-ai/utils.ts (Modified: 2 additions, 1 deletion, 3 changes)

🧪 Test Added

N/A (This specific PR does not introduce new tests in its patch. However, comprehensive unit tests covering schema dereferencing and transformation for complex nested schemas were added in a related previous pull request, ensuring this fix is adequately covered.)

🔒Security Vulnerabilities

N/A

Motivation

This PR is motivated by the need to ensure the robustness and correctness of schema transformation logic for Gemini function calling, especially for complex nested data structures. Adding comprehensive tests helps prevent regressions and ensures reliable interaction with the Gemini API.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)

How Has This Been Tested?

  • Unit Tests
  • Integration Tests
  • Manual Testing

Screenshots (if applicable)

N/A

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Tip

Quality Recommendations

  1. Consider refining the type definition for node to avoid the (node as any) assertion, if feasible, to improve type safety and maintainability.

Tanka Poem ♫

Schema's deep maze,
Refs now flatten, clear and true,
Gemini can see.
No more tangled, nested forms,
Data flows, a perfect stream. ✨

Copy link
Contributor

@matter-code-review matter-code-review bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds a useful utility function to transform JSON schemas for Gemini tool parameters. The implementation looks solid, but I have a few suggestions to improve error handling and code clarity.

@narengogi narengogi requested a review from b4s36t4 July 23, 2025 11:01
Copy link
Collaborator

@narengogi narengogi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend adding comprehensive test cases for this function as it is in the critical path of the requests.
Please add test cases with all supported combinators like anyOf, allOf and not, etc.

} else if (
key === 'anyOf' &&
Array.isArray(value) &&
value.length === 2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does google not support nullable field with more than one anyOf fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, type: null is not allowed. If we want to imply a field is optional, we should use nullable field

References I found:

  1. Nullable types not supported in pydantic classes for json googleapis/python-genai#62
  2. https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/function-calling#schema
Input should be 'TYPE_UNSPECIFIED', 'STRING', 'NUMBER', 'INTEGER', 'BOOLEAN', 'ARRAY' or 'OBJECT'
image

Copy link
Contributor

Important

PR Review Skipped

PR review skipped as per the configuration setting. Run a manually review by commenting /matter review

💡Tips to use Matter AI

Command List

  • /matter summary: Generate AI Summary for the PR
  • /matter review: Generate AI Reviews for the latest commit in the PR
  • /matter review-full: Generate AI Reviews for the complete PR
  • /matter release-notes: Generate AI release-notes for the PR
  • /matter : Chat with your PR with Matter AI Agent
  • /matter remember : Generate AI memories for the PR
  • /matter explain: Get an explanation of the PR
  • /matter help: Show the list of available commands and documentation
  • Need help? Join our Discord server: https://discord.gg/fJU5DvanU3

Copy link
Contributor

Important

PR Review Skipped

PR review skipped as per the configuration setting. Run a manually review by commenting /matter review

💡Tips to use Matter AI

Command List

  • /matter summary: Generate AI Summary for the PR
  • /matter review: Generate AI Reviews for the latest commit in the PR
  • /matter review-full: Generate AI Reviews for the complete PR
  • /matter release-notes: Generate AI release-notes for the PR
  • /matter : Chat with your PR with Matter AI Agent
  • /matter remember : Generate AI memories for the PR
  • /matter explain: Get an explanation of the PR
  • /matter help: Show the list of available commands and documentation
  • Need help? Join our Discord server: https://discord.gg/fJU5DvanU3

Comment on lines -52 to -54
if (Object.hasOwn(schema, '$schema')) {
delete schema['$schema'];
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been already handled at recursivelyDeleteUnsupportedParameters, hence removed

@pnkvalavala pnkvalavala changed the title fix: dereference schemas to remove $defs/$ref for Gemini tool parameters fix: handle complex nested schemas in gemini function calling & structured outputs Jul 27, 2025
Copy link
Contributor

Important

PR Review Skipped

PR review skipped as per the configuration setting. Run a manually review by commenting /matter review

💡Tips to use Matter AI

Command List

  • /matter summary: Generate AI Summary for the PR
  • /matter review: Generate AI Reviews for the latest commit in the PR
  • /matter review-full: Generate AI Reviews for the complete PR
  • /matter release-notes: Generate AI release-notes for the PR
  • /matter : Chat with your PR with Matter AI Agent
  • /matter remember : Generate AI memories for the PR
  • /matter explain: Get an explanation of the PR
  • /matter help: Show the list of available commands and documentation
  • Need help? Join our Discord server: https://discord.gg/fJU5DvanU3

@pnkvalavala
Copy link
Contributor Author

I would recommend adding comprehensive test cases for this function as it is in the critical path of the requests. Please add test cases with all supported combinators like anyOf, allOf and not, etc.

@narengogi have added tests, updated code to fix both function calling and structured outputs
You can find all the info and testing code with a complex schema which should almost cover all types of scenarios. Please take a look

for (const [key, value] of Object.entries(node)) {
if (key === 'enum' && Array.isArray(value)) {
transformed.enum = value;
transformed.format = 'enum';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont see enum as supported format in vertex docs

Image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tested by removing format: enum customization for both function calling and structured outputs that I followed from instructor library, it worked. Hence removed and updated PR

Copy link
Contributor

Important

PR Review Skipped

PR review skipped as per the configuration setting. Run a manually review by commenting /matter review

💡Tips to use Matter AI

Command List

  • /matter summary: Generate AI Summary for the PR
  • /matter review: Generate AI Reviews for the latest commit in the PR
  • /matter review-full: Generate AI Reviews for the complete PR
  • /matter release-notes: Generate AI release-notes for the PR
  • /matter : Chat with your PR with Matter AI Agent
  • /matter remember : Generate AI memories for the PR
  • /matter explain: Get an explanation of the PR
  • /matter help: Show the list of available commands and documentation
  • Need help? Join our Discord server: https://discord.gg/fJU5DvanU3

Comment on lines +221 to +224
if (stack.has(defKey)) return node;
stack.add(defKey);
const resolved = derefer(target, activeDefs, stack);
stack.delete(defKey);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think stack will always be empty here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stack won’t always be empty here, it is shared across the current recursion path. After stack.delete(defKey) we’ve only removed the current key, but ancestor defKeys from higher frames can still be in the set.

Example:
A -> B -> A (cycle)
After B finishes: stack = {A}

if (defKey && target) {
if (stack.has(defKey)) return node;
stack.add(defKey);
const resolved = derefer(target, activeDefs, stack);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please correct me if wrong, the derefered object won't be having any $ref key right? atleast I didn't find from the schema you've attached in test file. Why are we trying to derefer the object with no $ref.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

target itself often has no $ref at the root, but it can contain nested $refs. I updated the tests to show this: contact -> $ref: ContactInfo, and inside ContactInfo we have address: { $ref: "#/$defs/PostalAddress" }.

Calling derefer(target, …) walks that subtree so contact.properties.address becomes an inline object.
If a def truly has no nested refs (like Job / StatusEnum), the call is a quick no-op (we just traverse its fields and return unchanged), but it keeps the behavior consistent.

const defKey = getDefFromRef(node.$ref);
const target = getDefObject(activeDefs, defKey);
if (defKey && target) {
if (stack.has(defKey)) return node;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if at all stack is not empty and we find defKey in stack we're returning node, does node we're returning is already defered?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This return is intentional, when stack.has(defKey) is true, it means we have hit a cycle. We keep the $ref to avoid infinite recursion rather than fully expanding it again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example:

from pydantic import BaseModel

class A(BaseModel):
    field1: Optional["A"] = None

schema = A.model_json_schema()
const schema = {
  "$defs": {
      "A": {
          "properties": {
              "field1": {
                  "anyOf": [
                      {"$ref": "#/$defs/A"},
                      {"type": "null"}
                  ],
                  "default": null
              }
          },
          "title": "A",
          "type": "object"
      }
  },
  "$ref": "#/$defs/A"
};

let derefed = derefer(schema);
delete derefed.$defs;
console.log(JSON.stringify(derefed, null, 2));
{
  "properties": {
    "field1": {
      "anyOf": [
        {
          "properties": {
            "field1": {
              "anyOf": [ {"$ref": "#/$defs/A"}, {"type": "null"} ],
              "default": null
            }
          },
          "title": "A",
          "type": "object"
        },
        { "type": "null" }
      ],
      "default": null
    }
  },
  "title": "A",
  "type": "object"
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, I think this will still cause failure at vertex as anyOf is not at all supported.

Copy link
Contributor

Important

PR Review Skipped

PR review skipped as per the configuration setting. Run a manually review by commenting /matter review

💡Tips to use Matter AI

Command List

  • /matter summary: Generate AI Summary for the PR
  • /matter review: Generate AI Reviews for the latest commit in the PR
  • /matter review-full: Generate AI Reviews for the complete PR
  • /matter release-notes: Generate AI release-notes for the PR
  • /matter : Chat with your PR with Matter AI Agent
  • /matter remember : Generate AI memories for the PR
  • /matter explain: Get an explanation of the PR
  • /matter help: Show the list of available commands and documentation
  • Need help? Join our Discord server: https://discord.gg/fJU5DvanU3

@narengogi narengogi requested a review from b4s36t4 August 11, 2025 09:19
const keys = Object.keys(node);
if (keys.length === 1) return resolved;
const { $ref: _, ...siblings } = node;
return derefer({ ...resolved, ...siblings }, activeDefs, stack);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this intended or not, why do we need to de-ref the siblings when there's a loop for every iteration that does part? L231-235(diff), am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, fixed it. Thanks for flagging.

Copy link
Contributor

@b4s36t4 b4s36t4 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added few comments, but rest looks good to me.

Copy link
Contributor

Important

PR Review Skipped

PR review skipped as per the configuration setting. Run a manually review by commenting /matter review

💡Tips to use Matter AI

Command List

  • /matter summary: Generate AI Summary for the PR
  • /matter review: Generate AI Reviews for the latest commit in the PR
  • /matter review-full: Generate AI Reviews for the complete PR
  • /matter release-notes: Generate AI release-notes for the PR
  • /matter : Chat with your PR with Matter AI Agent
  • /matter remember : Generate AI memories for the PR
  • /matter explain: Get an explanation of the PR
  • /matter help: Show the list of available commands and documentation
  • Need help? Join our Discord server: https://discord.gg/fJU5DvanU3

@VisargD VisargD merged commit 25a09c6 into Portkey-AI:main Aug 15, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants