Structured output
Use structured output when your program needs data it can rely on, such as a recipe, search filters, or a classification result. Define a Nim type, ask the model for that type, and use the validated value directly.
Generate a typed value
Section titled “Generate a typed value”Define the shape you need, then call generateObject with that type:
import std/osimport nimgentimport nimgent/providers/openai
type Recipe = object name: string servings: int ingredients: seq[string]
let model = openAI(getEnv("OPENAI_API_KEY")).model("gpt-4o-mini")let result = generateObject[Recipe]( model, prompt = "Create a weeknight lasagna recipe for four people.")
echo result.value.nameecho result.value.servingsfor ingredient in result.value.ingredients: echo ingredientSave the example as recipe.nim, then run it with:
OPENAI_API_KEY=... nim c -r recipe.nimresult.value is a Recipe, not a JSON string. nimgent validates the model’s
JSON against the type before returning it.
Shape the result
Section titled “Shape the result”Use ordinary Nim objects, sequences, enums, and nested types to describe the data you need:
type Difficulty = enum easy, medium, hard
type Ingredient = object name: string quantity: string
type Recipe = object name: string difficulty: Difficulty ingredients: seq[Ingredient]The model must choose one of the enum values and return each nested ingredient with its declared fields.
Use Option[T] for data that may be absent:
import std/options
type Recipe = object name: string notes: Option[string]When notes has no value, the model returns null and you receive none.
This form works with strict native structured output. Use jsonOptional only
when a field must be omitted entirely, because native mode may reject schemas
with omitted fields.
Add constraints and descriptions
Section titled “Add constraints and descriptions”Field pragmas help the model produce useful values and reject invalid ones:
type Review = object headline {.jsonDescription: "A short, neutral headline.", jsonMinLength: 3, jsonMaxLength: 80.}: string score {.jsonMinimum: 1, jsonMaximum: 5.}: int sourceUrl {.jsonPattern: "^https?://".}: stringYou can combine pragmas on one field:
| Pragma | Effect |
|---|---|
jsonDescription |
Adds guidance for the model about what the field should contain. |
jsonMinimum / jsonMaximum |
Sets inclusive numeric bounds. |
jsonMinLength / jsonMaxLength |
Sets the minimum or maximum number of characters in a string. |
jsonPattern |
Requires a string to match a regular expression. |
jsonMinItems / jsonMaxItems |
Sets the minimum or maximum number of items in a sequence. |
jsonOptional |
Allows the model to omit the field entirely. Use Option[T] when null is acceptable and native structured output matters. |
Use descriptions for requirements that are easier to express in words than with a type.
Use a runtime schema
Section titled “Use a runtime schema”If the schema comes from configuration or another service, pass a JsonNode
instead of a Nim type:
import std/json
let schema = %*{ "type": "object", "properties": {"answer": {"type": "string"}}, "required": ["answer"]}
let result = generateObject( model, schema = schema, prompt = "Give a one-word answer.")
echo result.value["answer"].getStrThe returned value is a JsonNode. If you have a matching Nim type later, use
result.toObject[YourType] to decode the validated value.
Choose an output mode
Section titled “Choose an output mode”Leave mode at its default, omAuto, unless you have a specific requirement:
| Mode | Use it when |
|---|---|
omAuto |
You want native structured output where available, with JSON text as a fallback. This is the default. |
omNative |
The provider must enforce the schema natively. It fails before a request if the provider or schema is incompatible. |
omJson |
You need JSON text output rather than a provider-native format. |
omTool |
Your model works best when it submits the result through a tool call. The schema root must be an object. |
For example, require native structured output with:
let result = generateObject[Recipe]( model, prompt = "Create a weeknight lasagna recipe.", mode = omNative)Repair invalid output
Section titled “Repair invalid output”If a response is not valid for your schema, maxRepairs gives the model extra
attempts to correct it:
let result = generateObject[Recipe]( model, prompt = "Create a weeknight lasagna recipe.", maxRepairs = 1)Each repair is another model call. The default is 0, so use repairs when a
strict schema is more important than the additional latency and cost.
If no attempt succeeds, generateObject raises ObjectError:
try: discard generateObject[Recipe](model, prompt = "Create a recipe.")except ObjectError as error: for issue in error.issueDetails: echo issue.path, ": ", issue.messageStream a partial object
Section titled “Stream a partial object”Use streamObject when your interface should show fields while the model is
still building the object:
import std/[json, os]
let result = streamObject[Recipe]( model, prompt = "Create a weeknight lasagna recipe.", onPartial = proc (partial: JsonNode): bool = if "name" in partial: stdout.write "\rRecipe: " & partial["name"].getStr flushFile(stdout) true)
echo ""echo result.value.nameonPartial receives best-effort JSON as it arrives. It may be incomplete or
not yet valid, so use result.value after streamObject returns for the final
validated value. Return false from onPartial to cancel the stream.
Troubleshooting
Section titled “Troubleshooting”- Native mode rejects the schema: use
omAuto, or remove schema features that the provider’s native format cannot express.jsonOptionalis one common cause. - The model returns an invalid value: add field descriptions or constraints,
then consider
maxRepairsfor important responses. - The response is cut off: increase
maxTokens. By default, a response truncated at the token limit is rejected rather than treated as a complete object. - You need an omitted field instead of
null: usejsonOptional, keeping in mind that it may not work withomNative.
Next steps
Section titled “Next steps”- Streaming to show text and tool activity as it arrives.
- Tools and agents to use the same typed inputs for tools.
- Providers to choose a provider and its options.