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' },
})