> ## Documentation Index
> Fetch the complete documentation index at: https://docs.liquid.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Function Calling

> Tool use with LeapFunction — same API on every platform, with Hermes and Pythonic parsers.

Function calling lets the model invoke predefined functions provided by your app — query an API, run a calculation, fetch external state. Register `LeapFunction` definitions on the `Conversation`, run generation as usual, and the model's tool-call tokens come back as `MessageResponse.functionCalls`.

<Warning>
  Not all models support function calling. Check the model card on [Hugging Face](https://huggingface.co/LiquidAI) before assuming a checkpoint supports tool use.
</Warning>

<Info>
  Vision and audio-capable models require companion files. Bundles embed these references; GGUF checkpoints expect siblings such as `mmproj-*.gguf` (vision) and audio decoder/tokenizer files. When detected, you can attach `ChatMessageContent.image` / `.audio` parts to your tool responses as well as user messages.
</Info>

## Register functions on the conversation

`Conversation.registerFunction` takes a `LeapFunction` instance describing name, parameters, and purpose.

<Tabs>
  <Tab title="Swift (iOS / macOS)">
    ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
    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)"
          ),
        ]
      )
    )
    ```
  </Tab>

  <Tab title="Kotlin (all platforms)">
    ```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
    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.LeapStr(),
            description = "The city name",
          ),
          LeapFunctionParameter(
            name = "unit",
            type = LeapFunctionParameterType.LeapStr(
              enumValues = listOf("celsius", "fahrenheit")
            ),
            description = "Temperature unit",
          ),
        ),
      ),
    )
    ```
  </Tab>
</Tabs>

Use normal identifiers — letters, underscores, and digits (not starting with a digit). Most models trained for tool use recognize that shape.

<Info>
  The Kotlin parameter type classes are named with a `Leap` prefix (`LeapStr`, `LeapNum`, `LeapInt`, `LeapBool`, `LeapArr`, `LeapObj`, `LeapNull`) to avoid collisions with Kotlin's built-in `String`, `Number`, `Int`, `Boolean`, etc. The Swift bindings expose the same primitives under cleaner names (`.string(...)`, `.number(...)`, etc.) via SKIE.
</Info>

## Handle the response

Function calls arrive as `MessageResponse.functionCalls` (Swift) / `MessageResponse.FunctionCalls` (Kotlin), which wraps a list of `LeapFunctionCall`.

<Tabs>
  <Tab title="Swift (iOS / macOS)">
    ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
    public struct LeapFunctionCall {
      public let name: String
      public let arguments: [String: Any?]
    }
    ```

    ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
    let userMessage = ChatMessage(role: .user, content: [.text("What's the weather in NYC?")])

    for try await response in conversation.generateResponse(message: userMessage) {
        switch onEnum(of: response) {
        case .functionCalls(let payload):
            for call in payload.functionCalls {
                print("Function call: \(call.name) \(call.arguments)")
                // dispatch to your tool implementation
            }
        case .chunk, .reasoningChunk, .audioSample, .complete:
            break
        }
    }
    ```
  </Tab>

  <Tab title="Kotlin (all platforms)">
    ```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
    data class LeapFunctionCall(
        val name: String,
        val arguments: Map<String, Any?>,
    )
    ```

    ```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
    conversation.generateResponse(userMessage).onEach { response ->
        when (response) {
            is MessageResponse.Chunk -> { /* text chunk */ }
            is MessageResponse.FunctionCalls -> {
                response.functionCalls.forEach { call ->
                    Log.d(TAG, "Call ${call.name} with ${call.arguments}")
                    // dispatch to your tool implementation
                }
            }
            is MessageResponse.Complete -> {
                // Tool calls are also surfaced on the assembled assistant message:
                response.fullMessage.functionCalls?.forEach { /* ... */ }
            }
            else -> {}
        }
    }.collect()
    ```

    Tool calls are also attached to the final assistant message on the `Complete` event — useful if you'd rather batch-process tool calls once generation finishes.
  </Tab>
</Tabs>

The model may emit missing or invalid arguments — defensively validate the arguments map against your tool's expected shape before dispatching.

## Sending tool results back

Append the tool's output as a `tool`-role message and continue the conversation. Both platforms use the same shape — see also [Quick Start → Add tool results back to history](./quick-start).

<Tabs>
  <Tab title="Swift (iOS / macOS)">
    ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
    let toolMessage = ChatMessage(
      role: .tool,
      content: [.text(#"{"temperature":72,"conditions":"sunny"}"#)]
    )

    guard let current = conversation else { return }
    let updatedHistory = current.history + [toolMessage]
    conversation = current.modelRunner.createConversationFromHistory(history: updatedHistory)
    ```
  </Tab>

  <Tab title="Kotlin (all platforms)">
    ```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
    val toolMessage = ChatMessage(
        role = ChatMessage.Role.TOOL,
        content = listOf(
            ChatMessageContent.Text("""{"temperature":72,"conditions":"sunny"}""")
        )
    )

    val updatedHistory = conversation.history + toolMessage
    val nextConversation = modelRunner.createConversationFromHistory(updatedHistory)
    ```
  </Tab>
</Tabs>

Then call `generateResponse(...)` on the new conversation to get the model's tool-aware reply.

## `LeapFunction`

<Tabs>
  <Tab title="Swift (iOS / macOS)">
    ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
    public struct LeapFunction: Equatable {
      public let name: String
      public let description: String
      public let parameters: [LeapFunctionParameter]
    }

    public struct LeapFunctionParameter: Equatable {
      public let name: String
      public let type: LeapFunctionParameterType
      public let description: String
      public let optional: Bool
    }
    ```
  </Tab>

  <Tab title="Kotlin (all platforms)">
    ```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
    data class LeapFunction(
        val name: String,
        val description: String,
        val parameters: List<LeapFunctionParameter>,
    )

    data class LeapFunctionParameter(
        val name: String,
        val type: LeapFunctionParameterType,
        val description: String,
        val optional: Boolean = false,
    )
    ```
  </Tab>
</Tabs>

## Parameter types

`LeapFunctionParameterType` is translated into JSON Schema for the model. The same primitive set is exposed on both platforms.

<Tabs>
  <Tab title="Swift (iOS / macOS)">
    ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
    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`, `IntegerType` accept `enumValues` to constrain valid values.
    * `ArrayType` has `itemType` describing element type.
    * `ObjectType` has `properties: [String: LeapFunctionParameterType]` and `required: [String]`.
    * All non-`null` types take an optional `description` (only used when nested via `ArrayType.itemType` or object properties — when used directly as `LeapFunctionParameter.type`, the outer `description` wins).
  </Tab>

  <Tab title="Kotlin (all platforms)">
    ```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
    LeapFunctionParameterType.LeapStr(enumValues: List<String>? = null, description: String? = null)
    LeapFunctionParameterType.LeapNum(enumValues: List<Number>? = null, description: String? = null)
    LeapFunctionParameterType.LeapInt(enumValues: List<Int>? = null, description: String? = null)
    LeapFunctionParameterType.LeapBool(description: String? = null)
    LeapFunctionParameterType.LeapArr(itemType: LeapFunctionParameterType, description: String? = null)
    LeapFunctionParameterType.LeapObj(
        properties: Map<String, LeapFunctionParameterType>,
        required: List<String> = listOf(),
        description: String? = null,
    )
    LeapFunctionParameterType.LeapNull()
    ```

    The nested `description` is overridden when the type is used directly as `LeapFunctionParameter.type`; it's only consulted when the type is used inside `LeapArr.itemType` or `LeapObj.properties`.
  </Tab>
</Tabs>

### Example: array + enum parameters

<Tabs>
  <Tab title="Swift (iOS / macOS)">
    ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
    LeapFunction(
      name: "get_weather",
      description: "Query the weather of cities",
      parameters: [
        LeapFunctionParameter(
          name: "cities",
          type: LeapFunctionParameterType.array(
            ArrayType(itemType: .string(StringType()))
          ),
          description: "Names of the cities to query weather for"
        ),
        LeapFunctionParameter(
          name: "unit",
          type: LeapFunctionParameterType.string(
            StringType(enumValues: ["celsius", "fahrenheit"])
          ),
          description: "Temperature unit"
        ),
      ]
    )
    ```
  </Tab>

  <Tab title="Kotlin (all platforms)">
    ```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
    LeapFunction(
      name = "get_weather",
      description = "Get the weather forecast of cities",
      parameters = listOf(
        LeapFunctionParameter(
          name = "cities",
          type = LeapFunctionParameterType.LeapArr(
            itemType = LeapFunctionParameterType.LeapStr()
          ),
          description = "City names to query",
        ),
        LeapFunctionParameter(
          name = "temperature_unit",
          type = LeapFunctionParameterType.LeapStr(
            enumValues = listOf("Fahrenheit", "Celsius", "Kelvin")
          ),
          description = "Units for temperature",
        ),
      ),
    )
    ```
  </Tab>
</Tabs>

## Function call parser

Different models emit tool-call tokens in different shapes. The parser translates those tokens into `LeapFunctionCall` values. The default `LFMFunctionCallParser` handles Liquid Foundation Model (LFM2) Pythonic-style control tokens (`<|tool_call_start|> ... <|tool_call_end|>`). For Qwen3 and other models using the [Hermes function-calling format](https://github.com/NousResearch/Hermes-Function-Calling), use `HermesFunctionCallParser`.

<Tabs>
  <Tab title="Swift (iOS / macOS)">
    ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
    var options = GenerationOptions()
    options.functionCallParser = HermesFunctionCallParser()

    for try await response in conversation.generateResponse(
      message: userMessage,
      generationOptions: options
    ) {
      // ...
    }
    ```
  </Tab>

  <Tab title="Kotlin (all platforms)">
    ```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
    val options = GenerationOptions.build {
        functionCallParser = HermesFunctionCallParser()
    }

    conversation.generateResponse(userMessage, options).onEach { /* ... */ }.collect()
    ```
  </Tab>
</Tabs>

Pass `null` / `nil` as the parser to disable tool-call parsing entirely — the raw tool-call text is then surfaced as ordinary `Chunk`s.
