Skip to content

feat: Structured Output support#231

Merged
kylexqian merged 3 commits intomainfrom
kyle/structured-output
Apr 10, 2026
Merged

feat: Structured Output support#231
kylexqian merged 3 commits intomainfrom
kyle/structured-output

Conversation

@kylexqian
Copy link
Copy Markdown
Collaborator

Added structured output support for chat. Tested all model providers + streaming.

We now also include the usage as part of the TextGenerationOutput. This was nice because it led to finding a bug in usage reporting for gemini in streaming.

Unit tests included as well

Added structured output support for chat. Tested all model providers + streaming.

We now also include the usage as part of the TextGenerationOutput. This was nice because it led to finding a bug in usage reporting for gemini in streaming.

Unit tests included as well
Signed-off-by: Kyle Qian <kylexqian@gmail.com>
@kylexqian
Copy link
Copy Markdown
Collaborator Author

Based on 3rd party PR #169

@kylexqian
Copy link
Copy Markdown
Collaborator Author

Testing output

Structured output integration tests
Server : https://3.133.152.176
Schema : name (str) + age (int) + occupation (str)


-- OpenAI --------------------------------------------------
  [PASS]  OpenAI  | non-streaming | json_object  (prompt=24, completion=244)
         {
  "name": "Elena Marquez",
  "age": 32,
  "gender": "Female",
  "occupation": "Wildlife Biologist",
  "nationality": "Spanish",
  "residence": "Barcelona, Spain",
  "background": {
    "education": "PhD in Ecology from the University of Barcelona",
    "family": {
      "parents": ["Carlos Marquez", "Lucia Marquez"],
      "siblings": ["Miguel Marquez (younger brother)"]
    }
  },
  "personality_traits": [
    "curious",
    "adventurous",
    "empathetic",
    "meticulous"
  ],
  "hobbies": [
    "birdwatching",
    "rock climbing",
    "photography",
    "reading mystery novels"
  ],
  "notable_achievements": [
    "Discovered a new species of butterfly in the Pyrenees",
    "Published research on Mediterranean wetland restoration",
    "Recipient of the European Young Scientist Award (2022)"
  ],
  "fun_fact": "Elena speaks four languages: Spanish, Catalan, English, and French."
}
  [PASS]  OpenAI  | non-streaming | json_schema  (prompt=57, completion=17)
         {"name":"Lila Montgomery","age":32,"occupation":"Wildlife Photographer"}
  [PASS]  OpenAI  | streaming     | json_object  (prompt=24, completion=247)
         {
  "name": "Elena Marquez",
  "age": 32,
  "gender": "Female",
  "occupation": "Wildlife Biologist",
  "nationality": "Spanish",
  "background": {
    "birthplace": "Valencia, Spain",
    "education": "PhD in Ecology from the University of Barcelona",
    "languages": ["Spanish", "English", "French"]
  },
  "personality_traits": [
    "curious",
    "adventurous",
    "empathetic",
    "meticulous"
  ],
  "hobbies": [
    "birdwatching",
    "photography",
    "rock climbing",
    "reading mystery novels"
  ],
  "notable_achievements": [
    "Discovered a new species of butterfly in the Pyrenees",
    "Published research on migratory patterns of European storks",
    "Founded a non-profit for wildlife conservation"
  ],
  "current_residence": "Granada, Spain",
  "family": {
    "parents": ["Carlos Marquez", "Lucia Marquez"],
    "siblings": ["Miguel Marquez (older brother)"]
  }
}
  [PASS]  OpenAI  | streaming     | json_schema  (prompt=57, completion=17)
         {"name":"Lila Montgomery","age":32,"occupation":"Wildlife Photographer"}

-- Anthropic -----------------------------------------------
  [PASS]  Anthropic | non-streaming | json_schema  (prompt=208, completion=22)
         {"name": "Elena Vasquez", "age": 34, "occupation": "Marine Biologist"}
  [PASS]  Anthropic | streaming     | json_schema  (prompt=208, completion=22)
         {"name": "Elena Vasquez", "age": 34, "occupation": "Marine Biologist"}
  [PASS]  Anthropic -- json_object raises ValueError locally
         Anthropic models do not support response_format type 'json_object'. Use ResponseFormat(type='json_schema', json_schema={...}) with an explicit schema instead.

-- Gemini --------------------------------------------------
  [PASS]  Gemini  | non-streaming | json_object  (prompt=14, completion=138)
         {
  "person": {
    "name": "Anya Sharma",
    "age": 32,
    "occupation": "Quantum Physicist",
    "address": {
      "street": "789 Nebula Drive",
      "city": "Aethelburg",
      "zipCode": "90210",
      "country": "Eldoria"
    },
    "interests": [
      "Astrophysics",
      "Classical Music",
      "Hiking",
      "Ancient Languages"
    ],
    "isFictional": true
  }
}
  [PASS]  Gemini  | non-streaming | json_schema  (prompt=9, completion=14)
         {"name":"Alice","age":30,"occupation":"Engineer"}
  [PASS]  Gemini  | streaming     | json_object  (prompt=14, completion=136)
         {
  "person": {
    "name": "Anya Sharma",
    "age": 32,
    "occupation": "Quantum Physicist",
    "address": {
      "street": "789 Nebula Drive",
      "city": "Aethelburg",
      "zipCode": "90210",
      "country": "Eldoria"
    },
    "interests": [
      "Astrophysics",
      "Classical Music",
      "Hiking",
      "Ancient Languages"
    ],
    "isFictional": true
  }
}
  [PASS]  Gemini  | streaming     | json_schema  (prompt=9, completion=24)
         {
"name": "Alice", "age": 30, "occupation": "Software Engineer"
}

-- xAI / Grok ----------------------------------------------
  [PASS]  Grok    | non-streaming | json_object  (prompt=707, completion=448)
         {
  "name": "Elara Voss",
  "age": 34,
  "occupation": "Archaeologist",
  "background": "Born in a remote village, Elara discovered ancient ruins as a child, sparking her lifelong passion for uncovering lost civilizations. She now travels the world, deciphering forgotten languages and artifacts.",
  "personality": "Adventurous, intelligent, and fiercely independent, with a dry wit and a knack for getting into trouble.",
  "appearance": "Tall with sun-kissed skin, curly auburn hair, and piercing green eyes, often dressed in practical expedition gear."
}
  [PASS]  Grok    | non-streaming | json_schema  (prompt=774, completion=305)
          {"name":"Elara Voss","age":34,"occupation":"Quantum Physicist"}
  [PASS]  Grok    | streaming     | json_object  (prompt=707, completion=387)
         {"name":"Elara Voss","age":28,"occupation":"Adventurer and Archaeologist","background":"Born in a small coastal town, Elara discovered her passion for ancient mysteries after finding a hidden artifact in her grandmother's attic. She now travels the world, uncovering lost civilizations and dodging perilous traps.","personality":"Curious, brave, and quick-witted, with a sarcastic sense of humor that helps her in tight spots.","appearance":"Tall with athletic build, long auburn hair often tied back, piercing green eyes, and a scar on her left cheek from a close encounter with a booby-trapped tomb."}
  [PASS]  Grok    | streaming     | json_schema  (prompt=774, completion=222)
          {"name":"Alice Wonderland","age":28,"occupation":"Detective"}

------------------------------------------------------------
Results: 15/15 passed

Token usage:
  OpenAI  | non-streaming | json_object                prompt=   24  completion=  244
  OpenAI  | non-streaming | json_schema                prompt=   57  completion=   17
  OpenAI  | streaming     | json_object                prompt=   24  completion=  247
  OpenAI  | streaming     | json_schema                prompt=   57  completion=   17
  Anthropic | non-streaming | json_schema              prompt=  208  completion=   22
  Anthropic | streaming     | json_schema              prompt=  208  completion=   22
  Anthropic -- json_object raises ValueError locally   n/a
  Gemini  | non-streaming | json_object                prompt=   14  completion=  138
  Gemini  | non-streaming | json_schema                prompt=    9  completion=   14
  Gemini  | streaming     | json_object                prompt=   14  completion=  136
  Gemini  | streaming     | json_schema                prompt=    9  completion=   24
  Grok    | non-streaming | json_object                prompt=  707  completion=  448
  Grok    | non-streaming | json_schema                prompt=  774  completion=  305
  Grok    | streaming     | json_object                prompt=  707  completion=  387
  Grok    | streaming     | json_schema                prompt=  774  completion=  222

Not including this in unit tests atm -- just testing from a script

@kylexqian kylexqian changed the title Structured Output support feature: Structured Output support Apr 4, 2026
@kylexqian kylexqian changed the title feature: Structured Output support feat: Structured Output support Apr 9, 2026
@kylexqian kylexqian merged commit f69cd1a into main Apr 10, 2026
9 checks passed
@kylexqian kylexqian deleted the kyle/structured-output branch April 10, 2026 07:21
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.

2 participants