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.
conversation.registerFunction(
LeapFunction(
name: "get_weather",
description: "Query the weather of a city",
parameters: [
LeapFunctionParameter(
name: "city",
type: LeapFunctionParameterType.string(StringType()),
description: "The city to query weather for"
),
LeapFunctionParameter(
name: "unit",
type: LeapFunctionParameterType.string(
StringType(enumValues: ["celsius", "fahrenheit"])),
description: "Temperature unit (celsius or fahrenheit)"
),
]
)
)
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 functionCall
enum value of MessageResponse
, which contains a list of function calls.
public enum MessageResponse {
case functionCall([LeapFunctionCall])
// ...
}
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.
public struct LeapFunctionCall {
public let name: String
public let arguments: [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:
for try await response in conversation.generateResponse(
userTextMessage: userMessage
) {
switch response {
case .functionCall(let calls):
for call in calls {
// process function call here
print("Function call: \(call.name), \(call.arguments)")
}
default:
// process other responses
break
}
}
Function Definition
Functions for models to call are defined by LeapFunction
instances. It has three fields:
public struct LeapFunction: Equatable {
public let name: String
public let description: String
public let parameters: [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 array to declare what arguments (parameters) this function accepts.
The items of parameters
are instances of LeapFunctionParameter
.
public struct LeapFunctionParameter: Equatable {
public let name: String
public let type: LeapFunctionParameterType
public let description: String
public let optional: Bool
}
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:
public indirect enum LeapFunctionParameterType: Codable, Equatable {
case string(StringType)
case number(NumberType)
case integer(IntegerType)
case boolean(BooleanType)
case array(ArrayType)
case object(ObjectType)
case null(NullType)
}
StringType
, NumberType
and IntegerType
have a field of enumValues
to restrict the legit values for this parameter.
ArrayType
has a field of itemType
to describe the type of array itesm.
ObjectType
has a properties
field, which is a map from the property names (String
) to the property types (LeapFunctionParameterType
).
It also has a required
field, which is an array of names of required properties.
Each enum value includes a struct to describe the type. All type except NullType
has an optional field of description
to describe the purpose of this type.
It will be overrided if it is directly used as LeapFunctionParameter.type
. It only plays a role if the type instance is used as ArrayType.itemType
or the types
in object properties.
Here is a more comprehensive example on defining a function:
LeapFunction(
name: "get_weather",
description: "Query the weather of cities",
parameters: [
LeapFunctionParameter(
name: "cities",
type: LeapFunctionParameterType.array(ArrayType(
itemType: LeapFunctionParameterType.string(StringType()
)),
description: "Names of the cities to query weather for"
),
LeapFunctionParameter(
name: "unit",
type: LeapFunctionParameterType.string(
StringType(enumValues: ["celsius", "fahrenheit"])),
description: "Temperature unit (celsius or fahrenheit)"
),
]
)
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:
let options = GenerationOptions()
options.functionCallParser = HermesFunctionCallParser()
for try await response in conversation.generateResponse(
userTextMessage: userMessage,
generationOptions: options
) {
// process message response here
}