Documentation
¶
Overview ¶
Package schema derives JSON Schema from Go types and emits provider-specific dialects. Reflection-based frameworks all trip on the same wall: the schema reflection emits ($ref/$defs, missing additionalProperties) gets rejected by OpenAI strict mode and Gemini, so everyone reinvents a sanitizer. We emit INLINE (no $ref/$defs — recursion-safe) and provide dialect transforms so the tool author never hand-edits JSON Schema.
Tools expose the neutral schema (For); each model adapter dialectizes it at request time (Anthropic accepts the neutral form; OpenAI needs OpenAIStrict; Gemini needs Gemini).
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrGeminiUnsupported = errors.New("schema: not expressible in Gemini's schema subset")
ErrGeminiUnsupported reports a schema Gemini's schema subset cannot express without changing which documents it admits (a map, an unconstrained value, an object with no properties).
var ErrStrictUnsupported = errors.New("schema: not expressible in OpenAI strict mode")
ErrStrictUnsupported reports a schema OpenAIStrict cannot express in strict mode without changing which documents it admits (a map, a recursive type, a free-form object).
var ErrUnsupportedType = errors.New("schema: Go type cannot be described")
ErrUnsupportedType reports a Go type For cannot describe: a document valid under any schema for it would fail to decode, so For refuses the type rather than emit such a schema.
Functions ¶
func For ¶
func For[T any]() (json.RawMessage, error)
For derives an inline JSON Schema from Go type T. Nested structs are inlined (no $ref/$defs). Descriptions come from the `desc` tag. A field is required unless it is a pointer or its json tag has ",omitempty" or ",omitzero".
It mirrors encoding/json's decoding of T exactly: the object's properties are the fields encoding/json reads, found by the same rules (json tag names, "-" and "-,", embedded structs promoted into the parent unless the tag names them, and Go's dominance rules for fields that embedding supplies more than once, where a tie removes the name altogether). A ",string" field is a JSON string holding the value. []byte is a base64 string but a byte array is an array of numbers; a Go array has exactly its length in items. time.Time is a date-time string; json.RawMessage and interface{} are unconstrained. A type with a custom marshaler is not reflected field by field (reflection cannot see the custom shape): encoding.TextMarshaler is a string, any other json.Marshaler is unconstrained. A recursive reference is cut off as an unconstrained object.
It returns an error wrapping ErrUnsupportedType for a type encoding/json cannot decode as any schema would describe it: a field reached through an embedded pointer to an unexported struct type, which encoding/json cannot allocate, or a json tag name encoding/json does not accept (one with a quote, a backslash, or another reserved character), which encoding/json reads differently depending on how it is built.
func Gemini ¶ added in v0.7.0
func Gemini(neutral json.RawMessage) (json.RawMessage, error)
Gemini transforms a neutral schema into the OpenAPI 3.0 subset Gemini reads in a function declaration's parameters and in responseSchema:
- A type list with "null" becomes the type plus nullable:true; null in an enum, or a {"type":"null"} branch of anyOf, becomes nullable:true too. A string const becomes a one-value enum.
- additionalProperties:false is dropped (Gemini objects admit only their properties). Validation keywords Gemini lacks (uniqueItems, exclusiveMinimum, exclusiveMaximum, multipleOf), annotations ($schema, $id, $comment, examples, readOnly, writeOnly, deprecated), and a format Gemini does not know for the type are dropped: they only guide the model, and the tool still checks what it receives.
- A construct Gemini cannot express is an error wrapping ErrGeminiUnsupported that names its location: an object with no properties or with additionalProperties (a map, whose keys are data; a recursive type, which For cuts off as an open object), a schema with no type (interface{} or json.RawMessage, which For leaves unconstrained), a list of several non-null types, a non-string enum or const, an array with no items, and any keyword outside the subset ($ref, oneOf, allOf, not, patternProperties, ...).
A tool with no arguments has no schema to send; the Gemini adapter omits its parameters rather than calling this with an empty object, which Gemini rejects.
func OpenAIStrict ¶
func OpenAIStrict(neutral json.RawMessage) (json.RawMessage, error)
OpenAIStrict transforms a neutral schema into OpenAI structured-output "strict" form, which closes every object (additionalProperties:false) and lists every property in required. It changes no set of accepted answers beyond that:
- A property the neutral schema leaves optional (a pointer or omitempty field) stays optional in meaning: it is required but nullable (type [T, "null"], and null added to an enum), so the model sends null rather than inventing a value. encoding/json decodes null into such a field as its zero value.
- An object strict mode cannot close without changing it (a map, whose keys are data; a recursive type, which For cuts off as an open object; any object with no declared properties or with additionalProperties or patternProperties) is an error wrapping ErrStrictUnsupported that names the offending location. Closing it would admit only {}, so every answer would arrive empty.
- An untyped schema ({}, which For gives any and json.RawMessage, or the schema true) admits every value, which strict mode cannot express; it is an error wrapping ErrStrictUnsupported too.
Operates on the inline schema.
Types ¶
This section is empty.