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

# Tool Calling

> Function calling with on-device LLMs on Android

## Overview

Tool Calling enables on-device LLMs to invoke registered functions — such as fetching weather, performing calculations, or querying APIs — and incorporate the results into their responses. The SDK handles the full orchestration loop: generate → parse tool call → execute → feed result back → generate final response.

<Note>
  Tool calling uses the `RunAnywhereToolCalling` class, **not** the `RunAnywhere` singleton. Make
  sure to import from the correct package.
</Note>

## Package Imports

```kotlin theme={null}
import com.runanywhere.sdk.public.extensions.LLM.RunAnywhereToolCalling
import com.runanywhere.sdk.public.extensions.LLM.ToolDefinition
import com.runanywhere.sdk.public.extensions.LLM.ToolParameter
import com.runanywhere.sdk.public.extensions.LLM.ToolParameterType
import com.runanywhere.sdk.public.extensions.LLM.ToolCallingOptions
import com.runanywhere.sdk.public.extensions.LLM.ToolValue
```

Or import the entire LLM extensions package:

```kotlin theme={null}
import com.runanywhere.sdk.public.extensions.LLM.*
```

## Basic Usage

```kotlin theme={null}
// 1. Register a tool
RunAnywhereToolCalling.registerTool(
    ToolDefinition(
        name = "get_weather",
        description = "Get current weather for a city",
        parameters = listOf(
            ToolParameter(
                name = "city",
                type = ToolParameterType.STRING,
                description = "City name",
                required = true
            )
        ),
        category = "Utility"
    )
) { args ->
    val city = args["city"]?.stringValue ?: "Unknown"
    mapOf(
        "temperature" to ToolValue.number(72.0),
        "condition" to ToolValue.string("sunny"),
        "city" to ToolValue.string(city)
    )
}

// 2. Generate with tools
val options = ToolCallingOptions(
    maxToolCalls = 3,
    autoExecute = true,
    temperature = 0.7f,
    maxTokens = 512
)

val result = RunAnywhereToolCalling.generateWithTools(
    "What's the weather in San Francisco?",
    options
)

println(result.text)        // "The weather in San Francisco is 72°F and sunny."
println(result.toolCalls)   // List of tool calls made
println(result.toolResults) // List of tool execution results
```

## API Reference

### RunAnywhereToolCalling

| Method                               | Return Type         | Description                                       |
| ------------------------------------ | ------------------- | ------------------------------------------------- |
| `registerTool(definition, executor)` | `Unit`              | Register a tool with its definition and callback  |
| `generateWithTools(prompt, options)` | `ToolCallingResult` | Run full orchestration: generate → call → respond |

### ToolDefinition

```kotlin theme={null}
data class ToolDefinition(
    val name: String,
    val description: String,
    val parameters: List<ToolParameter>,
    val category: String? = null
)
```

| Property      | Type                  | Description                             |
| ------------- | --------------------- | --------------------------------------- |
| `name`        | `String`              | Unique tool identifier used by the LLM  |
| `description` | `String`              | What the tool does (shown to the model) |
| `parameters`  | `List<ToolParameter>` | Input parameters the tool accepts       |
| `category`    | `String?`             | Optional grouping category              |

### ToolParameter

```kotlin theme={null}
data class ToolParameter(
    val name: String,
    val type: ToolParameterType,
    val description: String,
    val required: Boolean = false
)
```

| Property      | Type                | Description                                |
| ------------- | ------------------- | ------------------------------------------ |
| `name`        | `String`            | Parameter name                             |
| `type`        | `ToolParameterType` | Data type (`STRING`, `NUMBER`, `BOOLEAN`)  |
| `description` | `String`            | Parameter description (shown to the model) |
| `required`    | `Boolean`           | Whether the parameter is required          |

### ToolParameterType

| Value                       | Description       |
| --------------------------- | ----------------- |
| `ToolParameterType.STRING`  | String parameter  |
| `ToolParameterType.NUMBER`  | Numeric parameter |
| `ToolParameterType.BOOLEAN` | Boolean parameter |

### ToolCallingOptions

```kotlin theme={null}
data class ToolCallingOptions(
    val maxToolCalls: Int = 3,
    val autoExecute: Boolean = true,
    val temperature: Float = 0.7f,
    val maxTokens: Int = 512
)
```

| Property       | Type      | Default | Description                             |
| -------------- | --------- | ------- | --------------------------------------- |
| `maxToolCalls` | `Int`     | `3`     | Maximum tool invocations per generation |
| `autoExecute`  | `Boolean` | `true`  | Automatically execute tool calls        |
| `temperature`  | `Float`   | `0.7f`  | Creativity (0.0–2.0)                    |
| `maxTokens`    | `Int`     | `512`   | Maximum tokens to generate              |

### ToolCallingResult

| Property      | Type               | Description                                    |
| ------------- | ------------------ | ---------------------------------------------- |
| `text`        | `String`           | Final response text incorporating tool results |
| `toolCalls`   | `List<ToolCall>`   | All tool calls the model made                  |
| `toolResults` | `List<ToolResult>` | Execution results for each tool call           |

### ToolCall

| Property    | Type                     | Description                        |
| ----------- | ------------------------ | ---------------------------------- |
| `toolName`  | `String`                 | Name of the tool the model invoked |
| `arguments` | `Map<String, ToolValue>` | Arguments passed by the model      |

### ToolResult

| Property  | Type                      | Description                            |
| --------- | ------------------------- | -------------------------------------- |
| `result`  | `Map<String, ToolValue>?` | Return value from the tool executor    |
| `error`   | `String?`                 | Error message if execution failed      |
| `success` | `Boolean`                 | Whether the tool executed successfully |

### ToolValue

Factory methods for creating typed values:

| Method                    | Input    | Description                  |
| ------------------------- | -------- | ---------------------------- |
| `ToolValue.string(value)` | `String` | Creates a string tool value  |
| `ToolValue.number(value)` | `Double` | Creates a numeric tool value |

Accessors for reading values:

| Property            | Return Type | Description               |
| ------------------- | ----------- | ------------------------- |
| `value.stringValue` | `String?`   | Extracts the string value |

## Examples

### Weather Tool

```kotlin theme={null}
RunAnywhereToolCalling.registerTool(
    ToolDefinition(
        name = "get_weather",
        description = "Get current weather for a city",
        parameters = listOf(
            ToolParameter(
                name = "city",
                type = ToolParameterType.STRING,
                description = "City name",
                required = true
            )
        ),
        category = "Utility"
    )
) { args ->
    val city = args["city"]?.stringValue ?: "Unknown"
    mapOf(
        "temperature" to ToolValue.number(72.0),
        "condition" to ToolValue.string("sunny"),
        "humidity" to ToolValue.number(65.0)
    )
}
```

### Calculator Tool

```kotlin theme={null}
RunAnywhereToolCalling.registerTool(
    ToolDefinition(
        name = "calculate",
        description = "Evaluate a math expression and return the result",
        parameters = listOf(
            ToolParameter(
                name = "expression",
                type = ToolParameterType.STRING,
                description = "Math expression (e.g., '2 + 3 * 4')",
                required = true
            )
        ),
        category = "Utility"
    )
) { args ->
    val expr = args["expression"]?.stringValue ?: "0"
    try {
        val engine = javax.script.ScriptEngineManager().getEngineByName("js")
        val result = engine.eval(expr) as Number
        mapOf("result" to ToolValue.number(result.toDouble()))
    } catch (e: Exception) {
        mapOf("error" to ToolValue.string("Invalid expression: $expr"))
    }
}
```

### Current Time Tool

```kotlin theme={null}
RunAnywhereToolCalling.registerTool(
    ToolDefinition(
        name = "get_current_time",
        description = "Get the current date and time",
        parameters = emptyList(),
        category = "Utility"
    )
) { _ ->
    val formatter = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
    val now = java.time.LocalDateTime.now().format(formatter)
    mapOf("datetime" to ToolValue.string(now))
}
```

### Multiple Tools Together

```kotlin theme={null}
fun registerAllTools() {
    // Register weather, calculator, and time tools (see above)
    registerWeatherTool()
    registerCalculatorTool()
    registerTimeTool()
}

suspend fun askWithTools(userPrompt: String): String {
    val options = ToolCallingOptions(
        maxToolCalls = 5,
        autoExecute = true,
        temperature = 0.5f,
        maxTokens = 1024
    )

    val result = RunAnywhereToolCalling.generateWithTools(userPrompt, options)
    return result.text
}

// The LLM will automatically choose which tools to call:
// "What's 15% of 250 and what time is it?" → calls calculate + get_current_time
```

### Jetpack Compose UI

A complete tool calling chat interface:

```kotlin theme={null}
@Composable
fun ToolCallingScreen() {
    var prompt by remember { mutableStateOf("") }
    var response by remember { mutableStateOf("") }
    var toolLog by remember { mutableStateOf("") }
    var isProcessing by remember { mutableStateOf(false) }
    val scope = rememberCoroutineScope()

    LaunchedEffect(Unit) {
        registerWeatherTool()
        registerCalculatorTool()
        registerTimeTool()
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        Text("Tool Calling Demo", style = MaterialTheme.typography.headlineMedium)

        OutlinedTextField(
            value = prompt,
            onValueChange = { prompt = it },
            label = { Text("Ask something...") },
            modifier = Modifier.fillMaxWidth(),
            placeholder = { Text("e.g., What's the weather in Tokyo?") }
        )

        Button(
            onClick = {
                scope.launch {
                    isProcessing = true
                    response = ""
                    toolLog = ""

                    val options = ToolCallingOptions(
                        maxToolCalls = 3,
                        autoExecute = true,
                        temperature = 0.7f,
                        maxTokens = 512
                    )

                    val result = RunAnywhereToolCalling.generateWithTools(prompt, options)

                    response = result.text

                    toolLog = result.toolCalls.joinToString("\n") { call ->
                        "Called: ${call.toolName}(${call.arguments.entries.joinToString { "${it.key}=${it.value.stringValue}" }})"
                    }

                    if (result.toolResults.isNotEmpty()) {
                        toolLog += "\n\nResults:\n"
                        toolLog += result.toolResults.joinToString("\n") { tr ->
                            if (tr.success) "  OK: ${tr.result}" else "  Error: ${tr.error}"
                        }
                    }

                    isProcessing = false
                }
            },
            enabled = prompt.isNotBlank() && !isProcessing,
            modifier = Modifier.fillMaxWidth()
        ) {
            Text(if (isProcessing) "Processing..." else "Ask with Tools")
        }

        if (response.isNotEmpty()) {
            Card(modifier = Modifier.fillMaxWidth()) {
                Column(modifier = Modifier.padding(16.dp)) {
                    Text("Response", style = MaterialTheme.typography.titleSmall)
                    Spacer(modifier = Modifier.height(8.dp))
                    Text(response)
                }
            }
        }

        if (toolLog.isNotEmpty()) {
            Card(
                modifier = Modifier.fillMaxWidth(),
                colors = CardDefaults.cardColors(
                    containerColor = MaterialTheme.colorScheme.surfaceVariant
                )
            ) {
                Column(modifier = Modifier.padding(16.dp)) {
                    Text("Tool Execution Log", style = MaterialTheme.typography.titleSmall)
                    Spacer(modifier = Modifier.height(8.dp))
                    Text(toolLog, fontFamily = FontFamily.Monospace, fontSize = 12.sp)
                }
            }
        }
    }
}

private fun registerWeatherTool() {
    RunAnywhereToolCalling.registerTool(
        ToolDefinition(
            name = "get_weather",
            description = "Get current weather for a city",
            parameters = listOf(
                ToolParameter(
                    name = "city",
                    type = ToolParameterType.STRING,
                    description = "City name",
                    required = true
                )
            ),
            category = "Utility"
        )
    ) { args ->
        val city = args["city"]?.stringValue ?: "Unknown"
        mapOf(
            "temperature" to ToolValue.number(72.0),
            "condition" to ToolValue.string("sunny"),
            "city" to ToolValue.string(city)
        )
    }
}

private fun registerCalculatorTool() {
    RunAnywhereToolCalling.registerTool(
        ToolDefinition(
            name = "calculate",
            description = "Evaluate a math expression",
            parameters = listOf(
                ToolParameter(
                    name = "expression",
                    type = ToolParameterType.STRING,
                    description = "Math expression (e.g., '2 + 3 * 4')",
                    required = true
                )
            ),
            category = "Utility"
        )
    ) { args ->
        val expr = args["expression"]?.stringValue ?: "0"
        try {
            val engine = javax.script.ScriptEngineManager().getEngineByName("js")
            val result = engine.eval(expr) as Number
            mapOf("result" to ToolValue.number(result.toDouble()))
        } catch (e: Exception) {
            mapOf("error" to ToolValue.string("Invalid expression"))
        }
    }
}

private fun registerTimeTool() {
    RunAnywhereToolCalling.registerTool(
        ToolDefinition(
            name = "get_current_time",
            description = "Get the current date and time",
            parameters = emptyList(),
            category = "Utility"
        )
    ) { _ ->
        val formatter = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
        val now = java.time.LocalDateTime.now().format(formatter)
        mapOf("datetime" to ToolValue.string(now))
    }
}
```

## Error Handling

```kotlin theme={null}
try {
    val result = RunAnywhereToolCalling.generateWithTools(prompt, options)

    // Check individual tool results for errors
    result.toolResults.forEach { toolResult ->
        if (!toolResult.success) {
            Log.w("ToolCalling", "Tool failed: ${toolResult.error}")
        }
    }

    displayResponse(result.text)
} catch (e: Exception) {
    when {
        e.message?.contains("model not loaded") == true -> {
            showError("Please load an LLM model before using tool calling.")
        }
        e.message?.contains("no tools registered") == true -> {
            showError("Register at least one tool before calling generateWithTools.")
        }
        else -> {
            showError("Tool calling error: ${e.message}")
        }
    }
}
```

<Warning>
  Tool executors run synchronously in the generation loop. Avoid long-running operations (network
  calls, heavy I/O) inside tool executors — they will block inference until complete.
</Warning>

## Best Practices

<Tip>
  * **Write clear descriptions** — the LLM uses `description` fields to decide when and how to call
    tools. Be specific about inputs and outputs. - **Keep tool responses small** — return only the
    data the LLM needs to formulate a response. Large payloads waste context window tokens. - **Set
    `maxToolCalls` conservatively** — prevents runaway loops where the model repeatedly calls tools. -
    **Validate arguments** — always handle missing or malformed arguments with defaults or error
    values rather than throwing exceptions. - **Use categories** — grouping tools by `category` helps
    organize related functionality.
</Tip>

## Related

<CardGroup cols={2}>
  <Card title="LLM Generation" icon="brain" href="/kotlin/llm/generate">
    Text generation
  </Card>

  <Card title="VLM" icon="eye" href="/kotlin/vlm">
    Vision Language Models
  </Card>

  <Card title="System Prompts" icon="robot" href="/kotlin/llm/system-prompts">
    Control AI behavior
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/kotlin/error-handling">
    Error handling patterns
  </Card>
</CardGroup>
