User Guide

Model Tool Requests

Define one sealed request hierarchy and one subtype per tool:

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import one.wabbit.openaischemas.FunctionSchema.Doc

@Serializable
sealed interface ToolRequest {
    @Doc("Generate a meme given a template and text.")
    @SerialName("GenerateMeme")
    @Serializable
    data class GenerateMeme(
        @Doc("The name of the meme template.")
        val templateName: String,
        @Doc("The text to put in each meme box.")
        val boxText: List<String>,
    ) : ToolRequest

    @Doc("List all available meme templates.")
    @SerialName("ListAllMemes")
    @Serializable
    data object ListAllMemes : ToolRequest
}

Use @SerialName values without dots. The generated OpenAI function name is the subtype serial name, and makeFunctions rejects dotted names.

Generate OpenAI Tools

import one.wabbit.openaischemas.FunctionSchema

val tools = FunctionSchema.makeFunctions(ToolRequest.serializer().descriptor)

check(tools.map { it.compiledToolSchema.function.name } == listOf("GenerateMeme", "ListAllMemes"))

Subtypes with constructor fields receive a parameters schema. Object subtypes with no fields are generated as parameterless tools.

Decode Function Calls

Use the same sealed request serializer to decode the model's selected function and arguments:

import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import one.wabbit.openaischemas.FunctionSchema

val request = FunctionSchema.parseFunctionCall(
    serializer = ToolRequest.serializer(),
    name = "GenerateMeme",
    arguments = JsonObject(
        mapOf(
            "templateName" to JsonPrimitive("classic"),
            "boxText" to JsonArray(listOf(JsonPrimitive("top"), JsonPrimitive("bottom"))),
        )
    ),
)

check(request is ToolRequest.GenerateMeme)

There are overloads for parsed JsonObject, raw JSON object strings, and the OpenAI SDK FunctionCall type.

Inspect Type Definitions

Use FunctionSchema.def<T>() when you need the intermediate schema tree instead of OpenAI SDK tool objects:

val typeDef = FunctionSchema.def<ToolRequest.GenerateMeme>()
val jsonSchema = typeDef.toJsonSchema()

TypeDef is useful for diagnostics and custom emitters, but it is not a complete JSON Schema AST.

Nullable Fields

Nullable object fields are treated as optional for required generation:

@Serializable
data class SaveNote(
    val title: String,
    val body: String?,
)

The generated object schema requires title but not body. The current JSON Schema lowering emits the underlying body type and does not add an explicit "null" union.