Function calling
Function calling allows the model to make requests to call some predefined functions provided by the app to interact with the environment.
Not all models support function calling. Please check the model card before using the model for function calling.
Register functions to conversations
To enable function calling, function definitions should be registered to the Conversation
instance before content generation.
Conversation.registerFunction
takes a LeapFunction
instance as the input, which describes the name, parameters and ability of the function.
val conversation = modelRunner.createConversation("You are a helpful assistant.")
conversation.registerFunction(
LeapFunction(
name = "get_weather",
description = "Get the weather forecast of a city",
parameters = listOf(
LeapFunctionParameter(
name = "city",
type = LeapFunctionParameterType.String(),
description = "The city name",
),
),
),
)
Generally speaking, function names and parameter names should be normal identifiers that are recongized by most common programming languages (e.g. Python, Javascript, etc). We recommend to use descriptive names that are composed by only letters, underscores and digits (not as the beginning).
Handle Function Calling Response
Function calling requests by the model will be presented as MessageResponse.FunctionCalls
instance, which contains a list of function calls.
data class FunctionCalls(val functionCalls: List<LeapFunctionCall>): MessageResponse
Each LeapFunctionCall
instance contains names and arguments of the function call request. The arguments
field is a map from String
to Any?
.
The app needs to check whether the required parameters are filled by the models. It is possible (even though very unlikely to happen) that some
parameters are missing or the function name is invalid.
data class LeapFunctionCall(
val name: String,
val arguments: Map<String, Any?>,
)
In order to handle the function call response, you will need to add a new branch to match responses from the generateResponse
flow:
conversation.generateResponse(userMessage).onEach { response ->
when (response) {
is MessageResponse.Chunk -> {
// process text chunk
}
is MessageResponse.FunctionCalls {
response.functionCalls.forEach { call ->
// Process function calls here
Log.d(TAG, "Call function: ${call.name}, arguments: ${call.arguments}")
}
}
else -> {
// other responses
}
}
The function calls will also be included in the assistant message generated by the model. So it is possible to delay the function call process until the generation process is done.
conversation.generateResponse(userMessage).onEach { response ->
when (response) {
is MessageResponse.Complete -> {
val assistantMessage = response.fullMessage
val functionCalls = assistantMessage.functionCalls
functionCalls?.forEach { call ->
// process function calls here
Log.d(TAG, "Call function: ${call.name}, arguments: ${call.arguments}")
}
}
else -> {
// process chunks
}
}
}
Function Definition
Functions for models to call are defined by LeapFunction
instances. It has three fields:
data class LeapFunction(
val name: String,
val description: String,
val parameters: List<LeapFunctionParameter>,
)
name
is the function name. It is recommneded to use only English letters, underscores and digits (not to start with digits) to compose the
function names because this format is supported by most models. description
tells the model what this function is doing. parameters
is
the ordered list to declare what arguments (parameters) this function accepts.
The items of parameters
are instances of LeapFunctionParameter
.
data class LeapFunctionParameter(
val name: String,
val type: LeapFunctionParameterType,
val description: String,
val optional: Boolean = false,
)
name
The name of the parameter.type
Data type of the parameter.description
Tells the model what this parameter is about.optional
Whether the function is optional.
LeapFunctionParameterType
describes the data types of the parameteres. They will be translated into JSON Schema for model to understand.
Following types are supported:
LeapFunctionParameterType.String(enumValues: List<kotlin.String>? = null, description: kotlin.String? = null)
LeapFunctionParameterType.Number(enumValues: List<kotlin.Number>? = null, description: kotlin.String? = null)
LeapFunctionParameterType.Integer(enumValues: List<Int>? = null, description: kotlin.String? = null)
LeapFunctionParameterType.Boolean(description: kotlin.String? = null)
LeapFunctionParameterType.Array(itemType: LeapFunctionParameterType, description: kotlin.String? = null)
LeapFunctionParameterType.Object(
properties: Map<kotlin.String, LeapFunctionParameterType>,
required: List<kotlin.String> = listOf(),
description: kotlin.String? = null,
)
String
: String literals.Number
: Number literals including integers and floating point numbers.Integer
: Integer literals.Boolean
: Boolean literals.Array
: Arrays of a defined type.itemType
parameter describes the data type of its items.Object
: Objects which has its own properties.properties
is the map between the property names to the property data types.required
list contains the name of all non-optional properties.
All fields accepts an optional parameter of description
, but it will be overrided if it is directly used as LeapFunctionParameter.type
. It only plays a role if
the type instance is used as Array.itemType
or the types in object properties.
For any type that accepts a parameter of enumValues
, this parameter provides the instructions to the models on the legit values to fill.
Here is a more comprehensive example on defining a function:
LeapFunction(
name = "get_weather",
description = "Get the weather forecast of cities",
parameters = listOf(
LeapFunctionParameter(
name = "cities",
type = LeapFunctionParameterType.Array(
itemType = LeapFunctionParameterType.String()
),
description = "City names to query",
),
LeapFunctionParameter(
name = "temperature_unit",
type = LeapFunctionParameterType.String(
enumValues = listOf(
"Fahrenheit", "Celsius", "Kelvin"
)
),
description = "Units for temperature",
),
),
)
Function Call Parser
Function call parsers are necessary to parse the function call request outputs from the model into LeapFunctionCall
data structure. Different models
generate function call requests in different styles, so we need to use the correct parser.
By default, LFMFunctionCallParser
is used. It supports Liquid Foundation Model (LFM2).
For Qwen3 models and other models that are using Hermes function calling format ,
apply HermesFunctionCallParser
by injecting a parser instance on the generation options:
val options = GenerationOptions.build {
functionCallParser = HermesFunctionCallParser()
}
conversation.generateResponse(userMessage, options).onEach {
// process message response here
}