Skip to main content

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.
Tool calling uses the RunAnywhereToolCalling class, not the RunAnywhere singleton. Make sure to import from the correct package.

Package Imports

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:
import com.runanywhere.sdk.public.extensions.LLM.*

Basic Usage

// 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

MethodReturn TypeDescription
registerTool(definition, executor)UnitRegister a tool with its definition and callback
generateWithTools(prompt, options)ToolCallingResultRun full orchestration: generate → call → respond

ToolDefinition

data class ToolDefinition(
    val name: String,
    val description: String,
    val parameters: List<ToolParameter>,
    val category: String? = null
)
PropertyTypeDescription
nameStringUnique tool identifier used by the LLM
descriptionStringWhat the tool does (shown to the model)
parametersList<ToolParameter>Input parameters the tool accepts
categoryString?Optional grouping category

ToolParameter

data class ToolParameter(
    val name: String,
    val type: ToolParameterType,
    val description: String,
    val required: Boolean = false
)
PropertyTypeDescription
nameStringParameter name
typeToolParameterTypeData type (STRING, NUMBER, BOOLEAN)
descriptionStringParameter description (shown to the model)
requiredBooleanWhether the parameter is required

ToolParameterType

ValueDescription
ToolParameterType.STRINGString parameter
ToolParameterType.NUMBERNumeric parameter
ToolParameterType.BOOLEANBoolean parameter

ToolCallingOptions

data class ToolCallingOptions(
    val maxToolCalls: Int = 3,
    val autoExecute: Boolean = true,
    val temperature: Float = 0.7f,
    val maxTokens: Int = 512
)
PropertyTypeDefaultDescription
maxToolCallsInt3Maximum tool invocations per generation
autoExecuteBooleantrueAutomatically execute tool calls
temperatureFloat0.7fCreativity (0.0–2.0)
maxTokensInt512Maximum tokens to generate

ToolCallingResult

PropertyTypeDescription
textStringFinal response text incorporating tool results
toolCallsList<ToolCall>All tool calls the model made
toolResultsList<ToolResult>Execution results for each tool call

ToolCall

PropertyTypeDescription
toolNameStringName of the tool the model invoked
argumentsMap<String, ToolValue>Arguments passed by the model

ToolResult

PropertyTypeDescription
resultMap<String, ToolValue>?Return value from the tool executor
errorString?Error message if execution failed
successBooleanWhether the tool executed successfully

ToolValue

Factory methods for creating typed values:
MethodInputDescription
ToolValue.string(value)StringCreates a string tool value
ToolValue.number(value)DoubleCreates a numeric tool value
Accessors for reading values:
PropertyReturn TypeDescription
value.stringValueString?Extracts the string value

Examples

Weather 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"),
        "humidity" to ToolValue.number(65.0)
    )
}

Calculator Tool

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

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

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:
@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

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}")
        }
    }
}
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.

Best Practices

  • 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.