Tool calling uses the
RunAnywhereTools class, not RunAnywhere. All tool registration and
generation methods live on RunAnywhereTools.Basic Usage
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
import 'package:runanywhere/runanywhere.dart';
2. Define Tool Executors
Every tool executor must match the signatureFuture<Map<String, ToolValue>> Function(Map<String, ToolValue>):
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
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,
);
Always call
RunAnywhereTools.clearTools() before registering tools to avoid duplicates when
hot-reloading during development.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 |
.stringValue getter:
final temp = args['temperature'];
print(temp?.stringValue); // "72.0"
Pattern Matching (Dart 3)
Use exhaustiveswitch expressions on the sealed class:
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
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
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
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: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
WrapgenerateWithTools in a try-catch and inspect individual ToolResult entries for per-tool errors:
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');
}
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.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
LLM Generation
Text generation with metrics
Streaming
Stream tokens in real-time
System Prompts
Control LLM behavior with system prompts
Error Handling
SDK-wide error handling patterns