OpenAPI Circular Schema Reference & Function Calling

What are the best practices for handling function calling when circular references are involved? I have attached image of an openapi spec that has a circular reference. How should I feed this to the function calling api?

2 Likes

If it can’t be unwrapped and expanded, I don’t think it can be done.

The function call AI-received language has a pretty limited “vocabulary” for placing functions. When strict, the function also has to form a grammar for enforcing JSON output, by object key name.

Infinitely deep JSON, whenever the AI might want, isn’t in the cards. You might consider the practical flattened schema that can specify the tool output you need that can be validated.

2 Likes

hmm I am unsure as to why my post was blocked. As long as graphs are well managed it shows a working system I wrote 20 years ago to fix this very issue soon after JSON came out.

I wont repost as I haven’t been given any information as to why it was blocked other than.

" Your post was flagged as off-topic"

As a known working solution it is hardly off-topic and as @arata says:

As I said in my post.

“This may not directly fix OpenAI’s issue but it may give you ideas on how you might overcome this issue.”

o1’s take on this:

Short Summary
Using this in a node-based architecture can be perfectly valid if you manage security, avoid unwanted cycles, and keep performance in check. As long as you understand the risks and plan the architecture thoroughly, this approach offers a flexible way to execute dynamic code.

It is likely that your post was plugging some GPTs that have no skill in creating structured output function schemas for OpenAI API models.

No lol I have tested a few GPTs to help beginners but clearly GPTs have issues. The GPTs I have written simply show methods that are replicatable in code. They are tests of the GPT system that show basic concepts programmers take for granted but others don’t understand. I have written these as they are intended, as an educational tool.

My post and 4 threads I have produced on the forum that show the structure of this system represents a complete working application I have written with over 30k lines of code.

I am not out to plug anything. I make no money/have no benefit from the solutions I post here. I post everything simply to help.

2 Likes

In my opinion OP should take a look at the underlying data structures and resolve the circular reference there. This appears to be a very basic problem that can be resolved by adding an abstraction layer, unifying the data into one table, keeping a duplicate of one of the tables, among many other approaches. Ultimately, I just don’t see any reason why the circular reference should be there. I could be wrong though?

@phyde1001
If you find the time I would be interested to learn how your suggested approach can be used to answer OP’s question.

I won’t get into the discussion about the flag here. That would be off-topic.

3 Likes

Welcome to the dev forum @andrewhlayer

You don’t feed an OpenAPI YAML schema directly for function calls. Instead, you define the specific functions (endpoints) you want the model to be able to call—along with their parameters—and let the model generate structured arguments for those functions.

Consider the schema you shared for reference.

Here’s how we can write function definitions for:

Listing all pets (no parameters)
{
    "type": "function",
    "function": {
      "name": "listAllPets",
      "description": "Retrieves a list of all pets in the Petstore. No parameters needed.",
      "strict": true,
      "parameters": {
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      }
    }
  }
Looking up an owner by Pet ID
{
    "type": "function",
    "function": {
      "name": "getOwnerForPet",
      "description": "Fetches the owner object for a specific Pet by pet_id.",
      "strict": true,
      "parameters": {
        "type": "object",
        "properties": {
          "pet_id": {
            "type": "integer",
            "description": "The unique ID of the Pet whose owner you want."
          }
        },
        "required": ["pet_id"],
        "additionalProperties": false
      }
    }
  }
Looking up pets by Owner ID
{
    "type": "function",
    "function": {
      "name": "getPetsByOwner",
      "description": "Fetches all pets belonging to a specific owner by owner_id.",
      "strict": true,
      "parameters": {
        "type": "object",
        "properties": {
          "owner_id": {
            "type": "integer",
            "description": "The unique ID of the Owner whose pets you want."
          }
        },
        "required": ["owner_id"],
        "additionalProperties": false
      }
    }
  }

Typically, you only need shallow relationships. If you want the model to get more details, you can chain these function calls. For instance, once you have the pet_id, you can pass it to the getOwnerForPet function; if you have an owner_id, you can call getPetsByOwner, etc.

How This Maps to the Original Schema

  1. listAllPets
    In your OpenAPI snippet, GET /pets simply returns all pets, so no parameters are needed. A function with no parameters is enough.

  2. getOwnerForPet
    If you need to look up an owner, you don’t necessarily embed the entire Owner object inside a Pet. You can define a dedicated function that takes a pet’s ID and returns the owner info.

  3. getPetsByOwner
    Conversely, if you have an owner_id and want to see their pets, define a function that returns all pets owned by them.

tl;dr

Rather than feeding the entire OpenAPI document to the model, you pick which real actions you want—list pets, get an owner, get an owner’s pets—and define concise function calling schemas for them. You can always chain them if you need multi-step queries.

2 Likes