Skip to content

ToolDescriptorSchemer

What is it

ToolDescriptorSchemer is a extension point that converts a ToolDescriptor into a JSON Schema object compatible with specific LLM providers.

Key points:

  • Location: ai.koog.agents.core.tools.serialization.ToolDescriptorSchemer
  • Contract: a single function scheme(toolDescriptor: ToolDescriptor): JsonObject
  • Implementations provided:
  • OpenAICompatibleToolDescriptorSchemer — generates schemas compatible with OpenAI‑style function/tool definitions.
  • OllamaToolDescriptorSchemer — generates schemas compatible with Ollama tool JSON.
// Interface
interface ToolDescriptorSchemaGenerator {
  fun generate(toolDescriptor: ToolDescriptor): JsonObject
}

Why to use it?

If you want to provide custom scheme for existing or new LLM providers, implement this interface to convert Koog’s ToolDescriptor into the expected JSON Schema format.

Implementation example

Below is a minimal custom implementation that renders only a subset of parameter types to illustrate how to plug into the SPI. Real implementations should cover all ToolParameterTypes (String, Integer, Float, Boolean, Null, Enum, List, Object, AnyOf).

class MinimalSchemer : ToolDescriptorSchemaGenerator {
    override fun generate(toolDescriptor: ToolDescriptor): JsonObject = buildJsonObject {
        put("type", "object")
        putJsonObject("properties") {
            (toolDescriptor.requiredParameters + toolDescriptor.optionalParameters).forEach { p ->
                put(p.name, buildJsonObject {
                    put("description", p.description)
                    when (val t = p.type) {
                        ToolParameterType.String -> put("type", "string")
                        ToolParameterType.Integer -> put("type", "integer")
                        is ToolParameterType.Enum -> {
                            put("type", "string")
                            putJsonArray("enum") { t.entries.forEach { add(JsonPrimitive(it)) } }
                        }
                        else -> put("type", "string") // fallback for brevity
                    }
                })
            }
        }
        putJsonArray("required") { toolDescriptor.requiredParameters.forEach { add(JsonPrimitive(it.name)) } }
    }
}

Example of usage with client

Typically you do not need to call a schemer directly. Koog clients accept a list of ToolDescriptor objects and apply the correct schemer internally when serializing requests for the provider.

The example below defines a simple tool and passes it to the OpenAI client. The client will use OpenAICompatibleToolDescriptorSchemer under the hood to build the JSON schema.

val client = OpenAILLMClient(apiKey = System.getenv("OPENAI_API_KEY"), toolsConverter = MinimalSchemer())

val getUserTool = ToolDescriptor(
    name = "get_user",
    description = "Returns user profile by id",
    requiredParameters = listOf(
        ToolParameterDescriptor(
            name = "id",
            description = "User id",
            type = ToolParameterType.String
        )
    )
)

val prompt = Prompt.build(id = "p1") { user("Hello") }
val responses = runBlocking {
    client.execute(
        prompt = prompt,
        model = OpenAIModels.Chat.GPT4o,
        tools = listOf(getUserTool)
    )
}

If you need direct access to the produced schema (for debugging or for a custom transport), you can instantiate the provider‑specific schemer and serialize the JSON yourself:

val json = Json { prettyPrint = true }
val schema = OpenAICompatibleToolDescriptorSchemaGenerator().generate(getUserTool())