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.
// 1. Register a toolRunAnywhereToolCalling.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 toolsval 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 madeprintln(result.toolResults) // List of tool execution results
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))}
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
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.
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.