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

> Register tools and let on-device LLMs call functions with structured arguments

Enable on-device LLMs to invoke your Dart functions by registering typed tool definitions. The SDK handles argument parsing, execution, and result formatting automatically.

<Warning>
  Tool calling uses the `RunAnywhereTools` class, **not** `RunAnywhere`. All tool registration and
  generation methods live on `RunAnywhereTools`.
</Warning>

## Basic Usage

```dart theme={null}
import 'package:runanywhere/runanywhere.dart';

// 1. Clear any previously registered tools
RunAnywhereTools.clearTools();

// 2. Register a tool
RunAnywhereTools.registerTool(
  const ToolDefinition(
    name: 'get_weather',
    description: 'Gets the current weather for a location',
    parameters: [
      ToolParameter(
        name: 'location',
        type: ToolParameterType.string,
        description: 'City name, e.g. "San Francisco"',
      ),
    ],
    category: 'Utility',
  ),
  _fetchWeather,
);

// 3. Generate with tools
final result = await RunAnywhereTools.generateWithTools(
  'What is the weather in San Francisco?',
  options: const ToolCallingOptions(
    maxToolCalls: 3,
    autoExecute: true,
    temperature: 0.7,
    maxTokens: 512,
  ),
);

print(result.text);        // "The weather in San Francisco is 72°F and sunny."
print(result.toolCalls);   // [ToolCall(toolName: 'get_weather', ...)]
print(result.toolResults); // [ToolResult(success: true, ...)]
```

## Setup

### 1. Import the SDK

```dart theme={null}
import 'package:runanywhere/runanywhere.dart';
```

### 2. Define Tool Executors

Every tool executor must match the signature `Future<Map<String, ToolValue>> Function(Map<String, ToolValue>)`:

```dart theme={null}
Future<Map<String, ToolValue>> _fetchWeather(
  Map<String, ToolValue> args,
) async {
  final location = args['location']?.stringValue ?? 'Unknown';

  // Call your weather service, local DB, or any async Dart code
  return {
    'temperature': NumberToolValue(72.0),
    'condition': StringToolValue('sunny'),
    'location': StringToolValue(location),
  };
}
```

### 3. Register Tools

```dart theme={null}
RunAnywhereTools.clearTools();

RunAnywhereTools.registerTool(
  const ToolDefinition(
    name: 'get_weather',
    description: 'Gets the current weather for a location',
    parameters: [
      ToolParameter(
        name: 'location',
        type: ToolParameterType.string,
        description: 'City name, e.g. "San Francisco"',
      ),
    ],
    category: 'Utility',
  ),
  _fetchWeather,
);
```

<Tip>
  Always call `RunAnywhereTools.clearTools()` before registering tools to avoid duplicates when
  hot-reloading during development.
</Tip>

## API Reference

### RunAnywhereTools

| Method                                 | Return Type                 | Description                                                          |
| -------------------------------------- | --------------------------- | -------------------------------------------------------------------- |
| `registerTool(definition, executor)`   | `void`                      | Register a tool with its definition and executor function            |
| `clearTools()`                         | `void`                      | Remove all registered tools                                          |
| `generateWithTools(prompt, {options})` | `Future<ToolCallingResult>` | Run the full orchestration loop: generate → parse → execute → repeat |

### ToolDefinition

| Property      | Type                  | Required | Description                                                             |
| ------------- | --------------------- | -------- | ----------------------------------------------------------------------- |
| `name`        | `String`              | Yes      | Unique identifier for the tool                                          |
| `description` | `String`              | Yes      | Human-readable description the LLM uses to decide when to call the tool |
| `parameters`  | `List<ToolParameter>` | Yes      | Typed parameter definitions                                             |
| `category`    | `String`              | No       | Logical grouping (e.g. `'Utility'`, `'Data'`)                           |

### ToolParameter

| Property      | Type                | Required | Description                                             |
| ------------- | ------------------- | -------- | ------------------------------------------------------- |
| `name`        | `String`            | Yes      | Parameter name                                          |
| `type`        | `ToolParameterType` | Yes      | One of `string`, `number`, `boolean`, `object`, `array` |
| `description` | `String`            | Yes      | What this parameter represents                          |

### ToolCallingOptions

| Property       | Type     | Default | Description                                                       |
| -------------- | -------- | ------- | ----------------------------------------------------------------- |
| `maxToolCalls` | `int`    | `5`     | Maximum tool invocations per generation                           |
| `autoExecute`  | `bool`   | `true`  | Automatically execute tool calls and feed results back to the LLM |
| `temperature`  | `double` | `0.8`   | Sampling temperature (0.0–2.0)                                    |
| `maxTokens`    | `int`    | `256`   | Maximum tokens in the final response                              |

### ToolCallingResult

| Property      | Type               | Description                                                                                                   |
| ------------- | ------------------ | ------------------------------------------------------------------------------------------------------------- |
| `text`        | `String`           | Final text response after all tool calls complete                                                             |
| `toolCalls`   | `List<ToolCall>`   | Every tool call the LLM requested; each has `toolName` (`String`) and `arguments` (`Map<String, ToolValue>`)  |
| `toolResults` | `List<ToolResult>` | Execution results; each has `result` (`Map<String, ToolValue>?`), `error` (`String?`), and `success` (`bool`) |

### ToolValue Types

`ToolValue` is a Dart 3 **sealed class**. Create instances with the concrete subtypes and read values with the `stringValue` getter or pattern matching.

| Subtype           | Constructor                | Description            |
| ----------------- | -------------------------- | ---------------------- |
| `StringToolValue` | `StringToolValue('hello')` | String value           |
| `NumberToolValue` | `NumberToolValue(42.0)`    | Numeric value (double) |
| `BoolToolValue`   | `BoolToolValue(true)`      | Boolean value          |
| `NullToolValue`   | `NullToolValue()`          | Explicit null          |
| `ArrayToolValue`  | `ArrayToolValue()`         | Array / list value     |
| `ObjectToolValue` | `ObjectToolValue()`        | Nested object value    |

Access any value as a string via the `.stringValue` getter:

```dart theme={null}
final temp = args['temperature'];
print(temp?.stringValue); // "72.0"
```

#### Pattern Matching (Dart 3)

Use exhaustive `switch` expressions on the sealed class:

```dart theme={null}
String formatToolValue(ToolValue value) {
  return switch (value) {
    StringToolValue sv => sv.stringValue,
    NumberToolValue nv => nv.value.toString(),
    BoolToolValue bv   => bv.value.toString(),
    NullToolValue()    => 'null',
    ArrayToolValue()   => '[array]',
    ObjectToolValue()  => '{object}',
  };
}
```

## Examples

### Weather Tool

```dart theme={null}
Future<Map<String, ToolValue>> _fetchWeather(
  Map<String, ToolValue> args,
) async {
  final location = args['location']?.stringValue ?? 'Unknown';
  // Simulate API call
  return {
    'temperature': NumberToolValue(72.0),
    'condition': StringToolValue('sunny'),
    'location': StringToolValue(location),
  };
}

RunAnywhereTools.registerTool(
  const ToolDefinition(
    name: 'get_weather',
    description: 'Gets the current weather for a location',
    parameters: [
      ToolParameter(
        name: 'location',
        type: ToolParameterType.string,
        description: 'City name',
      ),
    ],
    category: 'Utility',
  ),
  _fetchWeather,
);
```

### Calculator Tool

```dart theme={null}
Future<Map<String, ToolValue>> _calculate(
  Map<String, ToolValue> args,
) async {
  final a = (args['a'] as NumberToolValue?)?.value ?? 0;
  final b = (args['b'] as NumberToolValue?)?.value ?? 0;
  final op = args['operation']?.stringValue ?? 'add';

  final result = switch (op) {
    'add'      => a + b,
    'subtract' => a - b,
    'multiply' => a * b,
    'divide'   => b != 0 ? a / b : double.nan,
    _          => double.nan,
  };

  return {'result': NumberToolValue(result)};
}

RunAnywhereTools.registerTool(
  const ToolDefinition(
    name: 'calculate',
    description: 'Performs basic arithmetic on two numbers',
    parameters: [
      ToolParameter(
        name: 'a',
        type: ToolParameterType.number,
        description: 'First operand',
      ),
      ToolParameter(
        name: 'b',
        type: ToolParameterType.number,
        description: 'Second operand',
      ),
      ToolParameter(
        name: 'operation',
        type: ToolParameterType.string,
        description: 'One of: add, subtract, multiply, divide',
      ),
    ],
    category: 'Math',
  ),
  _calculate,
);
```

### Current Time Tool

```dart theme={null}
Future<Map<String, ToolValue>> _getTime(
  Map<String, ToolValue> args,
) async {
  final timezone = args['timezone']?.stringValue ?? 'UTC';
  final now = DateTime.now().toUtc();

  return {
    'time': StringToolValue(now.toIso8601String()),
    'timezone': StringToolValue(timezone),
  };
}

RunAnywhereTools.registerTool(
  const ToolDefinition(
    name: 'get_current_time',
    description: 'Returns the current date and time',
    parameters: [
      ToolParameter(
        name: 'timezone',
        type: ToolParameterType.string,
        description: 'IANA timezone, e.g. "America/New_York"',
      ),
    ],
    category: 'Utility',
  ),
  _getTime,
);
```

### Complete Flutter Widget

A full-screen widget that registers weather, calculator, and time tools, then runs a multi-tool conversation:

```dart theme={null}
import 'package:flutter/material.dart';
import 'package:runanywhere/runanywhere.dart';

class ToolCallingScreen extends StatefulWidget {
  const ToolCallingScreen({super.key});

  @override
  State<ToolCallingScreen> createState() => _ToolCallingScreenState();
}

class _ToolCallingScreenState extends State<ToolCallingScreen> {
  String _response = '';
  List<String> _toolLog = [];
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _registerTools();
  }

  void _registerTools() {
    RunAnywhereTools.clearTools();

    RunAnywhereTools.registerTool(
      const ToolDefinition(
        name: 'get_weather',
        description: 'Gets the current weather for a location',
        parameters: [
          ToolParameter(
            name: 'location',
            type: ToolParameterType.string,
            description: 'City name, e.g. "San Francisco"',
          ),
        ],
        category: 'Utility',
      ),
      _fetchWeather,
    );

    RunAnywhereTools.registerTool(
      const ToolDefinition(
        name: 'calculate',
        description: 'Performs basic arithmetic on two numbers',
        parameters: [
          ToolParameter(
            name: 'a',
            type: ToolParameterType.number,
            description: 'First operand',
          ),
          ToolParameter(
            name: 'b',
            type: ToolParameterType.number,
            description: 'Second operand',
          ),
          ToolParameter(
            name: 'operation',
            type: ToolParameterType.string,
            description: 'One of: add, subtract, multiply, divide',
          ),
        ],
        category: 'Math',
      ),
      _calculate,
    );

    RunAnywhereTools.registerTool(
      const ToolDefinition(
        name: 'get_current_time',
        description: 'Returns the current date and time',
        parameters: [
          ToolParameter(
            name: 'timezone',
            type: ToolParameterType.string,
            description: 'IANA timezone, e.g. "America/New_York"',
          ),
        ],
        category: 'Utility',
      ),
      _getTime,
    );
  }

  Future<Map<String, ToolValue>> _fetchWeather(
    Map<String, ToolValue> args,
  ) async {
    final location = args['location']?.stringValue ?? 'Unknown';
    return {
      'temperature': NumberToolValue(72.0),
      'condition': StringToolValue('sunny'),
      'location': StringToolValue(location),
    };
  }

  Future<Map<String, ToolValue>> _calculate(
    Map<String, ToolValue> args,
  ) async {
    final a = (args['a'] as NumberToolValue?)?.value ?? 0;
    final b = (args['b'] as NumberToolValue?)?.value ?? 0;
    final op = args['operation']?.stringValue ?? 'add';

    final result = switch (op) {
      'add'      => a + b,
      'subtract' => a - b,
      'multiply' => a * b,
      'divide'   => b != 0 ? a / b : double.nan,
      _          => double.nan,
    };

    return {'result': NumberToolValue(result)};
  }

  Future<Map<String, ToolValue>> _getTime(
    Map<String, ToolValue> args,
  ) async {
    final timezone = args['timezone']?.stringValue ?? 'UTC';
    final now = DateTime.now().toUtc();
    return {
      'time': StringToolValue(now.toIso8601String()),
      'timezone': StringToolValue(timezone),
    };
  }

  Future<void> _runPrompt(String prompt) async {
    setState(() {
      _isLoading = true;
      _response = '';
      _toolLog = [];
    });

    try {
      final result = await RunAnywhereTools.generateWithTools(
        prompt,
        options: const ToolCallingOptions(
          maxToolCalls: 3,
          autoExecute: true,
          temperature: 0.7,
          maxTokens: 512,
        ),
      );

      setState(() {
        _response = result.text;
        _toolLog = [
          for (final call in result.toolCalls)
            '→ ${call.toolName}(${call.arguments.map(
              (k, v) => MapEntry(k, v.stringValue),
            )})',
          for (final res in result.toolResults)
            res.success
                ? '✓ ${res.result}'
                : '✗ ${res.error}',
        ];
      });
    } catch (e) {
      setState(() => _response = 'Error: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Tool Calling Demo')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Wrap(
              spacing: 8,
              runSpacing: 8,
              children: [
                ElevatedButton(
                  onPressed: _isLoading
                      ? null
                      : () => _runPrompt(
                            'What is the weather in San Francisco?',
                          ),
                  child: const Text('Weather'),
                ),
                ElevatedButton(
                  onPressed: _isLoading
                      ? null
                      : () => _runPrompt('What is 42 * 17?'),
                  child: const Text('Calculate'),
                ),
                ElevatedButton(
                  onPressed: _isLoading
                      ? null
                      : () => _runPrompt('What time is it in UTC?'),
                  child: const Text('Time'),
                ),
              ],
            ),
            const SizedBox(height: 16),
            if (_isLoading) const LinearProgressIndicator(),
            const SizedBox(height: 16),
            if (_toolLog.isNotEmpty) ...[
              Text(
                'Tool Activity',
                style: Theme.of(context).textTheme.titleSmall,
              ),
              const SizedBox(height: 4),
              for (final log in _toolLog)
                Padding(
                  padding: const EdgeInsets.symmetric(vertical: 2),
                  child: Text(log, style: const TextStyle(fontFamily: 'monospace', fontSize: 13)),
                ),
              const Divider(height: 24),
            ],
            if (_response.isNotEmpty)
              Expanded(
                child: SingleChildScrollView(
                  child: Text(_response, style: const TextStyle(fontSize: 16)),
                ),
              ),
          ],
        ),
      ),
    );
  }
}
```

## Error Handling

Wrap `generateWithTools` in a try-catch and inspect individual `ToolResult` entries for per-tool errors:

```dart theme={null}
try {
  final result = await RunAnywhereTools.generateWithTools(
    prompt,
    options: const ToolCallingOptions(maxToolCalls: 3, autoExecute: true),
  );

  for (final res in result.toolResults) {
    if (!res.success) {
      debugPrint('Tool failed: ${res.error}');
    }
  }
} on RunAnywhereException catch (e) {
  debugPrint('SDK error: ${e.message}');
} catch (e) {
  debugPrint('Unexpected error: $e');
}
```

<Note>
  If a tool executor throws, the SDK catches the exception and records it in the corresponding
  `ToolResult.error` field. The LLM receives the error message and can retry or respond gracefully.
</Note>

### Common Errors

| Error                  | Cause                                     | Fix                                                 |
| ---------------------- | ----------------------------------------- | --------------------------------------------------- |
| `ToolNotFound`         | LLM requested an unregistered tool        | Verify tool names match exactly                     |
| `MaxToolCallsExceeded` | Hit the `maxToolCalls` limit              | Increase the limit or simplify the prompt           |
| `ModelNotLoaded`       | No LLM model loaded                       | Call `RunAnywhere.loadModel(...)` before generating |
| `InvalidArguments`     | Argument types don't match the definition | Check `ToolParameterType` in your `ToolDefinition`  |

## See Also

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

  <Card title="Streaming" icon="bolt" href="/flutter/llm/stream">
    Stream tokens in real-time
  </Card>

  <Card title="System Prompts" icon="robot" href="/flutter/llm/system-prompts">
    Control LLM behavior with system prompts
  </Card>

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