Skip to main content

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 lets the LLM decide when and how to invoke your functions, then incorporates the results into its response — all running on-device for maximum privacy.

Basic Usage

import { RunAnywhere, ToolDefinition, ToolCallingResult } from '@runanywhere/core'

// 1. Define a tool
const weatherTool: ToolDefinition = {
  name: 'get_weather',
  description: 'Get the current weather for a given city',
  parameters: [
    {
      name: 'city',
      type: 'string',
      description: 'The city name, e.g. "San Francisco"',
      required: true,
    },
  ],
}

// 2. Register it with an executor
RunAnywhere.registerTool(weatherTool, async (args) => {
  return { temperature: 72, condition: 'sunny' }
})

// 3. Generate with tools
const result: ToolCallingResult = await RunAnywhere.generateWithTools(
  'What is the weather in San Francisco?',
  { tools: [weatherTool], autoExecute: true }
)

console.log(result.text) // "The weather in San Francisco is 72°F and sunny."
console.log(result.toolCalls) // [{ toolName: 'get_weather', arguments: { city: 'San Francisco' } }]
console.log(result.toolResults) // [{ toolName: 'get_weather', success: true, result: { ... } }]

Setup

Defining Tools

Each tool is described by a ToolDefinition that tells the LLM what the tool does and what arguments it accepts.
import { ToolDefinition } from '@runanywhere/core'

const tools: ToolDefinition[] = [
  {
    name: 'get_weather',
    description: 'Get the current weather for a given city',
    parameters: [
      {
        name: 'city',
        type: 'string',
        description: 'The city name, e.g. "San Francisco"',
        required: true,
      },
      {
        name: 'unit',
        type: 'string',
        description: 'Temperature unit: "celsius" or "fahrenheit"',
        required: false,
        defaultValue: 'celsius',
        enum: ['celsius', 'fahrenheit'],
      },
    ],
  },
  {
    name: 'calculate',
    description: 'Evaluate a mathematical expression',
    parameters: [
      {
        name: 'expression',
        type: 'string',
        description: 'Math expression, e.g. "2 + 3 * 4"',
        required: true,
      },
    ],
  },
]

Registering & Clearing Tools

// Clear any previously registered tools
RunAnywhere.clearTools()

// Register each tool with its executor function
RunAnywhere.registerTool(tools[0], async (args: Record<string, unknown>) => {
  const city = args.city as string
  const unit = (args.unit as string) ?? 'celsius'
  // Call a weather API or return mock data
  return { temperature: unit === 'celsius' ? 22 : 72, condition: 'sunny', city }
})

RunAnywhere.registerTool(tools[1], async (args: Record<string, unknown>) => {
  const expr = args.expression as string
  try {
    const result = Function(`"use strict"; return (${expr})`)()
    return { result: Number(result) }
  } catch {
    return { error: 'Invalid expression' }
  }
})
Always call RunAnywhere.clearTools() before re-registering tools to avoid duplicates — for example, when your component remounts.

API Reference

RunAnywhere.registerTool()

Register a tool definition with its executor function.
RunAnywhere.registerTool(
  definition: ToolDefinition,
  executor: (args: Record<string, unknown>) => Promise<Record<string, unknown>>
): void
ParameterTypeDescription
definitionToolDefinitionThe tool’s name, description, and parameters
executor(args: Record<string, unknown>) => Promise<Record<string, unknown>>Async function invoked when the LLM calls this tool

RunAnywhere.clearTools()

Remove all registered tools.
RunAnywhere.clearTools(): void

RunAnywhere.generateWithTools()

Run a full tool-calling loop: generate → parse → execute → re-generate until the LLM produces a final text response or the call limit is reached.
await RunAnywhere.generateWithTools(
  prompt: string,
  options?: ToolCallingOptions
): Promise<ToolCallingResult>
ParameterTypeDescription
promptstringThe user’s input prompt
optionsToolCallingOptions?Generation and tool options

RunAnywhere.parseToolCall()

Manually parse raw LLM output to extract a tool call. Useful when you want full control over the tool-calling loop instead of using autoExecute.
await RunAnywhere.parseToolCall(
  llmOutput: string
): Promise<ParsedToolCall>
ParameterTypeDescription
llmOutputstringRaw text output from the LLM

Types

ToolDefinition

interface ToolDefinition {
  name: string
  description: string
  parameters: ToolParameter[]
}

ToolParameter

interface ToolParameter {
  name: string
  type: 'string' | 'number' | 'boolean' | 'object' | 'array'
  description: string
  required?: boolean
  defaultValue?: string
  enum?: string[]
}
FieldTypeDescription
namestringParameter name
typestringOne of string, number, boolean, object, array
descriptionstringHuman-readable description for the LLM
requiredboolean?Whether the parameter is required (default: false)
defaultValuestring?Default value when the parameter is omitted
enumstring[]?Allowed values — constrains the LLM’s choices

ToolCallingOptions

interface ToolCallingOptions {
  tools?: ToolDefinition[]
  maxToolCalls?: number
  autoExecute?: boolean
  temperature?: number
  maxTokens?: number
}
FieldTypeDefaultDescription
toolsToolDefinition[]?Override registered tools for this call
maxToolCallsnumber?5Maximum tool invocations per generation
autoExecuteboolean?trueAutomatically execute parsed tool calls
temperaturenumber?0.7Sampling temperature (0.0–2.0)
maxTokensnumber?256Maximum tokens to generate

ToolCallingResult

interface ToolCallingResult {
  text: string
  toolCalls: Array<{
    toolName: string
    arguments: Record<string, unknown>
  }>
  toolResults: Array<{
    toolName: string
    success: boolean
    result?: unknown
    error?: string
  }>
}
FieldTypeDescription
textstringFinal text response from the LLM
toolCallsArrayAll tool calls the LLM made during generation
toolResultsArrayExecution results for each tool call

ParsedToolCall

interface ParsedToolCall {
  toolCall?: {
    toolName: string
    arguments: Record<string, unknown>
  }
  text: string
}
FieldTypeDescription
toolCallobject?Extracted tool call, if the LLM invoked one
textstringRemaining text after tool-call extraction

Examples

Complete React Native Component

A full example with weather, calculator, and time tools wired into a chat-style UI.
ToolCallingDemo.tsx
import React, { useState, useCallback, useEffect } from 'react'
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  ScrollView,
  ActivityIndicator,
  StyleSheet,
} from 'react-native'
import {
  RunAnywhere,
  ToolDefinition,
  ToolCallingResult,
} from '@runanywhere/core'

const TOOLS: ToolDefinition[] = [
  {
    name: 'get_weather',
    description: 'Get the current weather for a given city',
    parameters: [
      { name: 'city', type: 'string', description: 'City name', required: true },
      {
        name: 'unit',
        type: 'string',
        description: 'Temperature unit',
        required: false,
        defaultValue: 'fahrenheit',
        enum: ['celsius', 'fahrenheit'],
      },
    ],
  },
  {
    name: 'calculate',
    description: 'Evaluate a mathematical expression',
    parameters: [
      { name: 'expression', type: 'string', description: 'Math expression', required: true },
    ],
  },
  {
    name: 'get_time',
    description: 'Get the current time in a given timezone',
    parameters: [
      {
        name: 'timezone',
        type: 'string',
        description: 'IANA timezone, e.g. "America/New_York"',
        required: true,
      },
    ],
  },
]

function registerAllTools(): void {
  RunAnywhere.clearTools()

  RunAnywhere.registerTool(TOOLS[0], async (args) => {
    const city = args.city as string
    const unit = (args.unit as string) ?? 'fahrenheit'
    const temp = unit === 'celsius' ? 22 : 72
    return { city, temperature: temp, unit, condition: 'partly cloudy' }
  })

  RunAnywhere.registerTool(TOOLS[1], async (args) => {
    const expr = args.expression as string
    try {
      const value = Function(`"use strict"; return (${expr})`)()
      return { expression: expr, result: Number(value) }
    } catch {
      return { expression: expr, error: 'Could not evaluate expression' }
    }
  })

  RunAnywhere.registerTool(TOOLS[2], async (args) => {
    const tz = args.timezone as string
    try {
      const time = new Date().toLocaleTimeString('en-US', { timeZone: tz })
      return { timezone: tz, currentTime: time }
    } catch {
      return { timezone: tz, error: 'Invalid timezone' }
    }
  })
}

interface Message {
  role: 'user' | 'assistant'
  text: string
  toolCalls?: ToolCallingResult['toolCalls']
}

export default function ToolCallingDemo(): React.JSX.Element {
  const [prompt, setPrompt] = useState('')
  const [messages, setMessages] = useState<Message[]>([])
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    registerAllTools()
  }, [])

  const handleSend = useCallback(async () => {
    const trimmed = prompt.trim()
    if (!trimmed || loading) return

    setMessages((prev) => [...prev, { role: 'user', text: trimmed }])
    setPrompt('')
    setLoading(true)

    try {
      const result = await RunAnywhere.generateWithTools(trimmed, {
        tools: TOOLS,
        maxToolCalls: 3,
        autoExecute: true,
        temperature: 0.7,
        maxTokens: 512,
      })

      setMessages((prev) => [
        ...prev,
        { role: 'assistant', text: result.text, toolCalls: result.toolCalls },
      ])
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Tool calling failed'
      setMessages((prev) => [...prev, { role: 'assistant', text: `Error: ${message}` }])
    } finally {
      setLoading(false)
    }
  }, [prompt, loading])

  return (
    <View style={styles.container}>
      <ScrollView style={styles.messages}>
        {messages.map((msg, i) => (
          <View key={i} style={msg.role === 'user' ? styles.userBubble : styles.aiBubble}>
            <Text style={styles.messageText}>{msg.text}</Text>
            {msg.toolCalls?.map((tc, j) => (
              <Text key={j} style={styles.toolCallText}>
                🔧 {tc.toolName}({JSON.stringify(tc.arguments)})
              </Text>
            ))}
          </View>
        ))}
        {loading && <ActivityIndicator style={styles.loader} />}
      </ScrollView>
      <View style={styles.inputRow}>
        <TextInput
          style={styles.input}
          value={prompt}
          onChangeText={setPrompt}
          placeholder="Ask about weather, math, or time..."
        />
        <TouchableOpacity style={styles.sendButton} onPress={handleSend} disabled={loading}>
          <Text style={styles.sendText}>Send</Text>
        </TouchableOpacity>
      </View>
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#f5f5f5' },
  messages: { flex: 1, padding: 16 },
  userBubble: {
    alignSelf: 'flex-end', backgroundColor: '#007AFF', borderRadius: 16,
    padding: 12, marginBottom: 8, maxWidth: '80%',
  },
  aiBubble: {
    alignSelf: 'flex-start', backgroundColor: '#fff', borderRadius: 16,
    padding: 12, marginBottom: 8, maxWidth: '80%',
    shadowColor: '#000', shadowOpacity: 0.05, shadowRadius: 4, elevation: 1,
  },
  messageText: { fontSize: 15, color: '#1a1a1a' },
  toolCallText: { fontSize: 12, color: '#888', marginTop: 4, fontFamily: 'monospace' },
  loader: { marginVertical: 12 },
  inputRow: { flexDirection: 'row', padding: 12, backgroundColor: '#fff' },
  input: {
    flex: 1, backgroundColor: '#f0f0f0', borderRadius: 20,
    paddingHorizontal: 16, paddingVertical: 10, fontSize: 15,
  },
  sendButton: {
    marginLeft: 8, backgroundColor: '#007AFF', borderRadius: 20,
    paddingHorizontal: 20, justifyContent: 'center',
  },
  sendText: { color: '#fff', fontWeight: '600' },
})

Manual Tool Call Parsing

Use parseToolCall() when you need to inspect or modify tool calls before execution.
const llmOutput = await RunAnywhere.generate('What is the weather in Tokyo?', {
  maxTokens: 256,
})

const parsed = await RunAnywhere.parseToolCall(llmOutput.text)

if (parsed.toolCall) {
  console.log('Tool:', parsed.toolCall.toolName) // "get_weather"
  console.log('Args:', parsed.toolCall.arguments) // { city: "Tokyo" }
  console.log('Remaining text:', parsed.text)

  // Execute manually, apply rate limits, log, etc.
} else {
  console.log('No tool call found — plain text response')
}

Multi-Step Tool Chain

The LLM can chain multiple tool calls in a single generation when maxToolCalls > 1.
const result = await RunAnywhere.generateWithTools(
  'What is 15% of the temperature in New York right now?',
  {
    tools: TOOLS,
    maxToolCalls: 3,
    autoExecute: true,
  }
)

// The LLM will:
// 1. Call get_weather({ city: "New York" })  → { temperature: 72 }
// 2. Call calculate({ expression: "72 * 0.15" }) → { result: 10.8 }
// 3. Return a final text answer

console.log(result.text)
// "15% of the current temperature in New York (72°F) is 10.8°F."
console.log(result.toolCalls.length) // 2

Error Handling

Tool executors should always return a result object — never throw. If a tool fails, return an object with an error field so the LLM can recover gracefully.
RunAnywhere.registerTool(weatherTool, async (args) => {
  const city = args.city as string

  try {
    const response = await fetch(`https://api.weather.example/v1?city=${encodeURIComponent(city)}`)
    if (!response.ok) {
      return { error: `Weather API returned ${response.status}` }
    }
    const data = await response.json()
    return { temperature: data.temp, condition: data.condition }
  } catch (err) {
    return { error: err instanceof Error ? err.message : 'Network request failed' }
  }
})

Common Error Scenarios

ScenarioCauseResolution
Tool not foundTool name in LLM output doesn’t match any registered toolVerify ToolDefinition.name matches exactly
Argument type mismatchLLM passes wrong type for a parameterCast defensively in the executor; provide clear description values
Max calls exceededLLM enters a tool-call loopLower maxToolCalls or refine tool descriptions
Executor timeoutExternal API call hangsAdd AbortController timeouts inside executors
No tool call parsedModel doesn’t emit a tool-call tokenUse a model that supports tool calling; check prompt clarity

Platform Differences

The React Native SDK uses RunAnywhere directly for tool calling — there is no separate ToolCalling class like the Web SDK.
FeatureReact Native (@runanywhere/core)Web (@runanywhere/web)
Entry pointRunAnywhere.registerTool()ToolCalling.registerTool()
Executor argument typeRecord<string, unknown>Record<string, ToolValue>
Manual parsingRunAnywhere.parseToolCall()Not available
Parameter enum fieldSupportedenumValues (different name)
Parameter defaultValueSupportedNot available

LLM Generation

Full text generation API

Streaming

Real-time token streaming

System Prompts

Control AI behavior

Error Handling

SDK error reference