diff --git a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt index cd26439e2..30fc5e4d2 100644 --- a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt +++ b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt @@ -313,6 +313,21 @@ class RunAnywhereApplication : Application() { framework = InferenceFramework.LLAMA_CPP, memoryRequirement = 400_000_000, ) + // LFM2-Tool models - For tool calling / function calling support + RunAnywhere.registerModel( + id = "lfm2-1.2b-tool-q4_k_m", + name = "LiquidAI LFM2 1.2B Tool Q4_K_M", + url = "https://huggingface.co/LiquidAI/LFM2-1.2B-Tool-GGUF/resolve/main/LFM2-1.2B-Tool-Q4_K_M.gguf", + framework = InferenceFramework.LLAMA_CPP, + memoryRequirement = 800_000_000, + ) + RunAnywhere.registerModel( + id = "lfm2-1.2b-tool-q8_0", + name = "LiquidAI LFM2 1.2B Tool Q8_0", + url = "https://huggingface.co/LiquidAI/LFM2-1.2B-Tool-GGUF/resolve/main/LFM2-1.2B-Tool-Q8_0.gguf", + framework = InferenceFramework.LLAMA_CPP, + memoryRequirement = 1_400_000_000, + ) Log.i("RunAnywhereApp", "โœ… LLM models registered") // Register ONNX STT and TTS models diff --git a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/models/ChatMessage.kt b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/models/ChatMessage.kt index 5efebbc73..d670af0a2 100644 --- a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/models/ChatMessage.kt +++ b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/models/ChatMessage.kt @@ -117,6 +117,19 @@ data class PerformanceSummary( val successRate: Double = 1.0, ) +/** + * App-local tool call info. + * Matches iOS ToolCallInfo exactly. + */ +@Serializable +data class ToolCallInfo( + val toolName: String, + val arguments: String, // JSON string for display + val result: String? = null, // JSON string for display + val success: Boolean, + val error: String? = null, +) + /** * App-specific ChatMessage for conversations. * Self-contained with app-local types. @@ -130,6 +143,7 @@ data class ChatMessage( val timestamp: Long = System.currentTimeMillis(), val analytics: MessageAnalytics? = null, val modelInfo: MessageModelInfo? = null, + val toolCallInfo: ToolCallInfo? = null, val metadata: Map? = null, ) { val isFromUser: Boolean get() = role == MessageRole.USER @@ -158,6 +172,7 @@ data class ChatMessage( thinkingContent: String? = null, analytics: MessageAnalytics? = null, modelInfo: MessageModelInfo? = null, + toolCallInfo: ToolCallInfo? = null, metadata: Map? = null, ): ChatMessage = ChatMessage( @@ -166,6 +181,7 @@ data class ChatMessage( thinkingContent = thinkingContent, analytics = analytics, modelInfo = modelInfo, + toolCallInfo = toolCallInfo, metadata = metadata, ) diff --git a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatScreen.kt b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatScreen.kt index f3e4fb9f4..05f83f182 100644 --- a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatScreen.kt +++ b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatScreen.kt @@ -44,7 +44,9 @@ import com.runanywhere.runanywhereai.data.ConversationStore import com.runanywhere.runanywhereai.domain.models.ChatMessage import com.runanywhere.runanywhereai.domain.models.Conversation import com.runanywhere.runanywhereai.domain.models.MessageRole +import com.runanywhere.runanywhereai.presentation.settings.ToolSettingsViewModel import com.runanywhere.runanywhereai.ui.theme.AppColors +import android.app.Application import com.runanywhere.runanywhereai.ui.theme.AppTypography import com.runanywhere.runanywhereai.ui.theme.Dimensions import kotlinx.coroutines.launch @@ -238,6 +240,20 @@ fun ChatScreen(viewModel: ChatViewModel = viewModel()) { color = MaterialTheme.colorScheme.outline, ) + // Tool calling indicator - matching iOS + val toolContext = LocalContext.current + val application = toolContext.applicationContext as Application + val toolSettingsViewModel = remember { ToolSettingsViewModel.getInstance(application) } + val toolState by toolSettingsViewModel.uiState.collectAsStateWithLifecycle() + + AnimatedVisibility( + visible = toolState.toolCallingEnabled && toolState.registeredTools.isNotEmpty(), + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically(), + ) { + ToolCallingBadge(toolCount = toolState.registeredTools.size) + } + // Model selection prompt (when no model loaded) - matching iOS AnimatedVisibility( visible = !uiState.isModelLoaded, @@ -457,6 +473,8 @@ fun MessageBubbleView( message: ChatMessage, isGenerating: Boolean = false, ) { + var showToolCallSheet by remember { mutableStateOf(false) } + val alignment = if (message.role == MessageRole.USER) { Arrangement.End @@ -498,6 +516,15 @@ fun MessageBubbleView( Spacer(modifier = Modifier.height(Dimensions.small)) } + // Tool call indicator (for assistant messages with tool calls) - matching iOS + if (message.role == MessageRole.ASSISTANT && message.toolCallInfo != null) { + com.runanywhere.runanywhereai.presentation.chat.components.ToolCallIndicator( + toolCallInfo = message.toolCallInfo, + onTap = { showToolCallSheet = true }, + ) + Spacer(modifier = Modifier.height(Dimensions.small)) + } + // Thinking toggle (if thinking content exists) - matching iOS message.thinkingContent?.let { thinking -> ThinkingToggle( @@ -650,6 +677,14 @@ fun MessageBubbleView( Spacer(modifier = Modifier.width(Dimensions.messageBubbleMinSpacing)) } } + + // Tool call detail sheet - matching iOS + if (showToolCallSheet && message.toolCallInfo != null) { + com.runanywhere.runanywhereai.presentation.chat.components.ToolCallDetailSheet( + toolCallInfo = message.toolCallInfo, + onDismiss = { showToolCallSheet = false }, + ) + } } // Helper function to format timestamp - matching iOS @@ -1118,6 +1153,42 @@ fun EmptyStateView( // MODEL SELECTION PROMPT // ==================== +/** + * Tool calling indicator badge - matching iOS ChatInterfaceView toolCallingBadge + */ +@Composable +fun ToolCallingBadge(toolCount: Int) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = Dimensions.mediumLarge, vertical = Dimensions.small), + horizontalArrangement = Arrangement.Center, + ) { + Row( + modifier = Modifier + .background( + color = AppColors.primaryAccent.copy(alpha = 0.1f), + shape = RoundedCornerShape(6.dp) + ) + .padding(horizontal = 10.dp, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp), + ) { + Icon( + imageVector = Icons.Default.Build, + contentDescription = "Tools enabled", + modifier = Modifier.size(10.dp), + tint = AppColors.primaryAccent, + ) + Text( + text = "Tools enabled ($toolCount)", + style = AppTypography.caption2, + color = AppColors.primaryAccent, + ) + } + } +} + @Composable fun ModelSelectionPrompt(onSelectModel: () -> Unit) { Surface( diff --git a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatViewModel.kt b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatViewModel.kt index 83ff83a0b..d8743e1ae 100644 --- a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatViewModel.kt +++ b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatViewModel.kt @@ -12,6 +12,8 @@ import com.runanywhere.runanywhereai.domain.models.Conversation import com.runanywhere.runanywhereai.domain.models.MessageAnalytics import com.runanywhere.runanywhereai.domain.models.MessageModelInfo import com.runanywhere.runanywhereai.domain.models.MessageRole +import com.runanywhere.runanywhereai.domain.models.ToolCallInfo +import com.runanywhere.sdk.public.extensions.LLM.ToolValue import com.runanywhere.sdk.public.RunAnywhere import com.runanywhere.sdk.public.events.EventBus import com.runanywhere.sdk.public.events.LLMEvent @@ -23,8 +25,19 @@ import com.runanywhere.sdk.public.extensions.generate import com.runanywhere.sdk.public.extensions.generateStream import com.runanywhere.sdk.public.extensions.isLLMModelLoaded import com.runanywhere.sdk.public.extensions.loadLLMModel +import com.runanywhere.sdk.public.extensions.LLM.ToolCallingOptions +import com.runanywhere.sdk.public.extensions.LLM.ToolCallFormat +import com.runanywhere.sdk.public.extensions.LLM.RunAnywhereToolCalling +import com.runanywhere.runanywhereai.presentation.settings.ToolSettingsViewModel import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.buildJsonObject import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterIsInstance @@ -202,7 +215,15 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { // Clear metrics from previous generation tokensPerSecondHistory.clear() - if (currentState.useStreaming) { + // Check if tool calling is enabled and tools are registered + val toolViewModel = ToolSettingsViewModel.getInstance(app) + val useToolCalling = toolViewModel.toolCallingEnabled + val registeredTools = RunAnywhereToolCalling.getRegisteredTools() + + if (useToolCalling && registeredTools.isNotEmpty()) { + Log.i(TAG, "๐Ÿ”ง Using tool calling with ${registeredTools.size} tools") + generateWithToolCalling(prompt, assistantMessage.id) + } else if (currentState.useStreaming) { generateWithStreaming(prompt, assistantMessage.id) } else { generateWithoutStreaming(prompt, assistantMessage.id) @@ -213,6 +234,90 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { } } + /** + * Generate with tool calling support + * Matches iOS generateWithToolCalling pattern + */ + private suspend fun generateWithToolCalling( + prompt: String, + messageId: String, + ) { + val startTime = System.currentTimeMillis() + + try { + // Detect the appropriate tool call format based on loaded model + // Note: loadedModelName can be null if model state changes during generation + val modelName = _uiState.value.loadedModelName + if (modelName == null) { + Log.w(TAG, "โš ๏ธ Tool calling initiated but model name is null, using default format") + } + val toolViewModel = ToolSettingsViewModel.getInstance(app) + val format = toolViewModel.detectToolCallFormat(modelName) + + Log.i(TAG, "๐Ÿ”ง Tool calling with format: $format for model: ${modelName ?: "unknown"}") + + // Create tool calling options + val toolOptions = ToolCallingOptions( + maxToolCalls = 3, + autoExecute = true, + temperature = 0.7f, + maxTokens = 1024, + format = format + ) + + // Generate with tools + val result = RunAnywhereToolCalling.generateWithTools(prompt, toolOptions) + val endTime = System.currentTimeMillis() + + // Update the assistant message with the result + val response = result.text + updateAssistantMessage(messageId, response, null) + + // Log tool calls and create tool call info + if (result.toolCalls.isNotEmpty()) { + Log.i(TAG, "๐Ÿ”ง Tool calls made: ${result.toolCalls.map { it.toolName }}") + result.toolResults.forEach { toolResult -> + Log.i(TAG, "๐Ÿ“‹ Tool result: ${toolResult.toolName} - success: ${toolResult.success}") + } + + // Create ToolCallInfo from the first tool call and result + val firstToolCall = result.toolCalls.first() + val firstToolResult = result.toolResults.firstOrNull { it.toolName == firstToolCall.toolName } + + val toolCallInfo = ToolCallInfo( + toolName = firstToolCall.toolName, + arguments = formatToolValueMapToJson(firstToolCall.arguments), + result = firstToolResult?.result?.let { formatToolValueMapToJson(it) }, + success = firstToolResult?.success ?: false, + error = firstToolResult?.error, + ) + + updateAssistantMessageWithToolCallInfo(messageId, toolCallInfo) + } + + // Create analytics + val analytics = createMessageAnalytics( + startTime = startTime, + endTime = endTime, + firstTokenTime = null, + thinkingStartTime = null, + thinkingEndTime = null, + inputText = prompt, + outputText = response, + thinkingText = null, + wasInterrupted = false, + ) + + updateAssistantMessageWithAnalytics(messageId, analytics) + + } catch (e: Exception) { + Log.e(TAG, "Tool calling failed", e) + throw e + } finally { + _uiState.value = _uiState.value.copy(isGenerating = false) + } + } + /** * Generate with streaming support and thinking mode * Matches iOS streaming generation pattern @@ -456,6 +561,23 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { _uiState.value = _uiState.value.copy(messages = updatedMessages) } + private fun updateAssistantMessageWithToolCallInfo( + messageId: String, + toolCallInfo: ToolCallInfo, + ) { + val currentMessages = _uiState.value.messages + val updatedMessages = + currentMessages.map { message -> + if (message.id == messageId) { + message.copy(toolCallInfo = toolCallInfo) + } else { + message + } + } + + _uiState.value = _uiState.value.copy(messages = updatedMessages) + } + /** * Create message analytics using app-local types */ @@ -724,6 +846,38 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { _uiState.value = _uiState.value.copy(error = null) } + /** + * Format a ToolValue map to JSON string for display. + * Uses kotlinx.serialization for proper JSON escaping of special characters. + */ + private fun formatToolValueMapToJson(map: Map): String { + val jsonObject = buildJsonObject { + map.forEach { (key, value) -> + put(key, formatToolValueToJsonElement(value)) + } + } + return Json.encodeToString(JsonObject.serializer(), jsonObject) + } + + /** + * Convert a ToolValue to the appropriate JsonElement type. + * Handles all ToolValue variants with proper JSON escaping. + */ + private fun formatToolValueToJsonElement(value: ToolValue): JsonElement { + return when (value) { + is ToolValue.StringValue -> JsonPrimitive(value.value) + is ToolValue.NumberValue -> JsonPrimitive(value.value) + is ToolValue.BoolValue -> JsonPrimitive(value.value) + is ToolValue.NullValue -> JsonNull + is ToolValue.ArrayValue -> buildJsonArray { + value.value.forEach { add(formatToolValueToJsonElement(it)) } + } + is ToolValue.ObjectValue -> buildJsonObject { + value.value.forEach { (k, v) -> put(k, formatToolValueToJsonElement(v)) } + } + } + } + companion object { private const val TAG = "ChatViewModel" } diff --git a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/components/ToolCallViews.kt b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/components/ToolCallViews.kt new file mode 100644 index 000000000..e97fb20b0 --- /dev/null +++ b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/components/ToolCallViews.kt @@ -0,0 +1,265 @@ +package com.runanywhere.runanywhereai.presentation.chat.components + +import androidx.compose.animation.core.* +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.dp +import com.runanywhere.runanywhereai.domain.models.ToolCallInfo +import com.runanywhere.runanywhereai.ui.theme.AppColors +import com.runanywhere.runanywhereai.ui.theme.AppTypography + +/** + * Tool call indicator button - matches iOS ToolCallIndicator + * Shows a small chip with the tool name that can be tapped to see details + */ +@Composable +fun ToolCallIndicator( + toolCallInfo: ToolCallInfo, + onTap: () -> Unit, + modifier: Modifier = Modifier, +) { + val backgroundColor = if (toolCallInfo.success) { + AppColors.primaryAccent.copy(alpha = 0.1f) + } else { + AppColors.primaryOrange.copy(alpha = 0.1f) + } + + val borderColor = if (toolCallInfo.success) { + AppColors.primaryAccent.copy(alpha = 0.3f) + } else { + AppColors.primaryOrange.copy(alpha = 0.3f) + } + + val iconTint = if (toolCallInfo.success) { + AppColors.primaryAccent + } else { + AppColors.primaryOrange + } + + Surface( + modifier = modifier + .clip(RoundedCornerShape(8.dp)) + .clickable { onTap() }, + color = backgroundColor, + shape = RoundedCornerShape(8.dp), + ) { + Row( + modifier = Modifier + .border(0.5.dp, borderColor, RoundedCornerShape(8.dp)) + .padding(horizontal = 10.dp, vertical = 6.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp), + ) { + Icon( + imageVector = if (toolCallInfo.success) Icons.Default.Build else Icons.Default.Warning, + contentDescription = null, + modifier = Modifier.size(12.dp), + tint = iconTint, + ) + Text( + text = toolCallInfo.toolName, + style = AppTypography.caption2, + color = AppColors.textSecondary, + maxLines = 1, + ) + } + } +} + +/** + * Tool call detail sheet - matches iOS ToolCallDetailSheet + * Shows full details of a tool call including arguments and results as JSON + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ToolCallDetailSheet( + toolCallInfo: ToolCallInfo, + onDismiss: () -> Unit, +) { + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = sheetState, + containerColor = MaterialTheme.colorScheme.surface, + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + .padding(bottom = 32.dp) + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(20.dp), + ) { + // Header + Text( + text = "Tool Call", + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.onSurface, + ) + + // Status section + StatusSection(success = toolCallInfo.success) + + // Tool name section + DetailSection(title = "Tool", content = toolCallInfo.toolName) + + // Arguments section + CodeSection(title = "Arguments", code = toolCallInfo.arguments) + + // Result section (if available) + toolCallInfo.result?.let { result -> + CodeSection(title = "Result", code = result) + } + + // Error section (if available) + toolCallInfo.error?.let { error -> + DetailSection(title = "Error", content = error, isError = true) + } + + Spacer(modifier = Modifier.height(16.dp)) + } + } +} + +@Composable +private fun StatusSection(success: Boolean) { + val backgroundColor = if (success) { + AppColors.statusGreen.copy(alpha = 0.1f) + } else { + AppColors.primaryRed.copy(alpha = 0.1f) + } + + val iconTint = if (success) AppColors.statusGreen else AppColors.primaryRed + + Row( + modifier = Modifier + .fillMaxWidth() + .background(backgroundColor, RoundedCornerShape(12.dp)) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp), + ) { + Icon( + imageVector = if (success) Icons.Default.CheckCircle else Icons.Default.Cancel, + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = iconTint, + ) + Text( + text = if (success) "Success" else "Failed", + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurface, + ) + } +} + +@Composable +private fun DetailSection( + title: String, + content: String, + isError: Boolean = false, +) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text( + text = title, + style = AppTypography.caption, + color = AppColors.textSecondary, + ) + Text( + text = content, + style = MaterialTheme.typography.bodyMedium, + color = if (isError) AppColors.primaryRed else MaterialTheme.colorScheme.onSurface, + ) + } +} + +@Composable +private fun CodeSection( + title: String, + code: String, +) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text( + text = title, + style = AppTypography.caption, + color = AppColors.textSecondary, + ) + Box( + modifier = Modifier + .fillMaxWidth() + .background( + MaterialTheme.colorScheme.surfaceVariant, + RoundedCornerShape(8.dp) + ) + .padding(12.dp) + .horizontalScroll(rememberScrollState()) + ) { + Text( + text = code, + style = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace), + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } +} + +/** + * Tool calling active indicator - matches iOS ToolCallingActiveIndicator + * Shows animated indicator when tool is being called + */ +@Composable +fun ToolCallingActiveIndicator( + modifier: Modifier = Modifier, +) { + val infiniteTransition = rememberInfiniteTransition(label = "rotation") + val rotation by infiniteTransition.animateFloat( + initialValue = 0f, + targetValue = 360f, + animationSpec = infiniteRepeatable( + animation = tween(2000, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ), + label = "rotation" + ) + + Row( + modifier = modifier + .background( + AppColors.primaryAccent.copy(alpha = 0.1f), + RoundedCornerShape(8.dp) + ) + .padding(horizontal = 10.dp, vertical = 6.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp), + ) { + Icon( + imageVector = Icons.Default.Settings, + contentDescription = null, + modifier = Modifier + .size(12.dp) + .graphicsLayer { rotationZ = rotation }, + tint = AppColors.primaryAccent, + ) + Text( + text = "Calling tool...", + style = AppTypography.caption2, + color = AppColors.textSecondary, + ) + } +} diff --git a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/settings/SettingsScreen.kt b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/settings/SettingsScreen.kt index 03c5bf793..e703e0dee 100644 --- a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/settings/SettingsScreen.kt +++ b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/settings/SettingsScreen.kt @@ -28,6 +28,8 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.runanywhere.runanywhereai.ui.theme.AppColors +import android.app.Application +import androidx.compose.ui.platform.LocalContext /** * Settings & Storage Screen @@ -114,6 +116,9 @@ fun SettingsScreen(viewModel: SettingsViewModel = viewModel()) { ) } + // Tool Calling Section + ToolSettingsSection() + // Storage Overview Section SettingsSection( title = "Storage Overview", @@ -631,3 +636,158 @@ private fun ApiConfigurationDialog( }, ) } + +// ============================================================================= +// Tool Settings Section +// ============================================================================= + +/** + * Tool Calling Settings Section + * + * Allows users to: + * - Enable/disable tool calling + * - Register demo tools (weather, time, calculator) + * - Clear all registered tools + * - View registered tools count + */ +@Composable +fun ToolSettingsSection() { + val context = LocalContext.current + val application = context.applicationContext as Application + val toolViewModel = remember { ToolSettingsViewModel.getInstance(application) } + val toolState by toolViewModel.uiState.collectAsStateWithLifecycle() + + SettingsSection(title = "Tool Calling") { + // Enable/Disable Toggle + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = "Enable Tool Calling", + style = MaterialTheme.typography.bodyLarge, + ) + Text( + text = "Allow LLMs to use registered tools", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + Switch( + checked = toolState.toolCallingEnabled, + onCheckedChange = { toolViewModel.setToolCallingEnabled(it) }, + colors = SwitchDefaults.colors( + checkedThumbColor = AppColors.primaryAccent, + checkedTrackColor = AppColors.primaryAccent.copy(alpha = 0.5f), + ), + ) + } + + if (toolState.toolCallingEnabled) { + HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp)) + + // Registered Tools Count + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = Icons.Outlined.Build, + contentDescription = null, + tint = AppColors.primaryAccent, + modifier = Modifier.size(20.dp), + ) + Text( + text = "Registered Tools", + style = MaterialTheme.typography.bodyMedium, + ) + } + Text( + text = "${toolState.registeredTools.size}", + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.Bold, + color = AppColors.primaryAccent, + ) + } + + // Tool List (if any) + if (toolState.registeredTools.isNotEmpty()) { + toolState.registeredTools.forEach { tool -> + Row( + modifier = Modifier + .fillMaxWidth() + .padding(start = 28.dp, top = 4.dp, bottom = 4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = "โ€ข ${tool.name}", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } + + HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp)) + + // Action Buttons + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + OutlinedButton( + onClick = { toolViewModel.registerDemoTools() }, + enabled = !toolState.isLoading, + colors = ButtonDefaults.outlinedButtonColors( + contentColor = AppColors.primaryGreen, + ), + modifier = Modifier.weight(1f), + ) { + Icon( + imageVector = Icons.Outlined.Add, + contentDescription = null, + modifier = Modifier.size(18.dp), + ) + Spacer(modifier = Modifier.width(4.dp)) + Text(if (toolState.isLoading) "Loading..." else "Add Demo Tools") + } + + if (toolState.registeredTools.isNotEmpty()) { + OutlinedButton( + onClick = { toolViewModel.clearAllTools() }, + enabled = !toolState.isLoading, + colors = ButtonDefaults.outlinedButtonColors( + contentColor = AppColors.primaryRed, + ), + ) { + Icon( + imageVector = Icons.Outlined.Delete, + contentDescription = null, + modifier = Modifier.size(18.dp), + ) + Spacer(modifier = Modifier.width(4.dp)) + Text("Clear") + } + } + } + + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "Demo tools: get_weather (Open-Meteo API), get_current_time, calculate", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } +} diff --git a/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/settings/ToolSettingsViewModel.kt b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/settings/ToolSettingsViewModel.kt new file mode 100644 index 000000000..ff97fafec --- /dev/null +++ b/examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/settings/ToolSettingsViewModel.kt @@ -0,0 +1,439 @@ +package com.runanywhere.runanywhereai.presentation.settings + +import android.app.Application +import android.content.Context +import android.util.Log +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope +import com.runanywhere.sdk.public.extensions.LLM.ToolDefinition +import com.runanywhere.sdk.public.extensions.LLM.ToolParameter +import com.runanywhere.sdk.public.extensions.LLM.ToolParameterType +import com.runanywhere.sdk.public.extensions.LLM.ToolValue +import com.runanywhere.sdk.public.extensions.LLM.ToolCallFormat +import com.runanywhere.sdk.public.extensions.LLM.RunAnywhereToolCalling +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLEncoder +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale +import java.util.TimeZone + +/** + * Tool Settings UI State + */ +data class ToolSettingsUiState( + val toolCallingEnabled: Boolean = false, + val registeredTools: List = emptyList(), + val isLoading: Boolean = false, +) + +/** + * Tool Settings ViewModel + * + * Manages tool calling configuration and demo tool registration. + * Mirrors iOS ToolSettingsViewModel.swift functionality. + */ +class ToolSettingsViewModel private constructor(application: Application) : AndroidViewModel(application) { + + private val _uiState = MutableStateFlow(ToolSettingsUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + private val prefs = application.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + + val toolCallingEnabled: Boolean + get() = _uiState.value.toolCallingEnabled + + companion object { + private const val TAG = "ToolSettingsVM" + private const val PREFS_NAME = "tool_settings" + private const val KEY_TOOL_CALLING_ENABLED = "tool_calling_enabled" + + /** Timeout for weather API requests (covers geocoding + weather fetch) */ + private const val WEATHER_API_TIMEOUT_MS = 15_000L + + @Volatile + private var instance: ToolSettingsViewModel? = null + + fun getInstance(application: Application): ToolSettingsViewModel { + return instance ?: synchronized(this) { + instance ?: ToolSettingsViewModel(application).also { instance = it } + } + } + } + + init { + // Load saved preference + val enabled = prefs.getBoolean(KEY_TOOL_CALLING_ENABLED, false) + _uiState.update { it.copy(toolCallingEnabled = enabled) } + + // Refresh registered tools + viewModelScope.launch { + refreshRegisteredTools() + } + } + + fun setToolCallingEnabled(enabled: Boolean) { + prefs.edit().putBoolean(KEY_TOOL_CALLING_ENABLED, enabled).apply() + _uiState.update { it.copy(toolCallingEnabled = enabled) } + } + + suspend fun refreshRegisteredTools() { + val tools = RunAnywhereToolCalling.getRegisteredTools() + _uiState.update { it.copy(registeredTools = tools) } + } + + /** + * Register demo tools matching iOS implementation: + * - get_weather: Uses Open-Meteo API (free, no API key) + * - get_current_time: Returns system time with timezone + * - calculate: Evaluates math expressions + */ + fun registerDemoTools() { + viewModelScope.launch { + _uiState.update { it.copy(isLoading = true) } + + try { + // Weather Tool - Uses Open-Meteo API (free, no API key required) + RunAnywhereToolCalling.registerTool( + definition = ToolDefinition( + name = "get_weather", + description = "Gets the current weather for a given location using Open-Meteo API", + parameters = listOf( + ToolParameter( + name = "location", + type = ToolParameterType.STRING, + description = "City name (e.g., 'San Francisco', 'London', 'Tokyo')", + required = true + ) + ), + category = "Utility" + ), + executor = { args: Map -> + fetchWeather((args["location"] as? ToolValue.StringValue)?.value ?: "San Francisco") + } + ) + + // Time Tool - Real system time with timezone + RunAnywhereToolCalling.registerTool( + definition = ToolDefinition( + name = "get_current_time", + description = "Gets the current date, time, and timezone information", + parameters = emptyList(), + category = "Utility" + ), + executor = { _: Map -> + val now = Date() + val dateFormatter = SimpleDateFormat("EEEE, MMMM d, yyyy 'at' h:mm:ss a", Locale.getDefault()) + val timeFormatter = SimpleDateFormat("HH:mm:ss", Locale.getDefault()) + val isoFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()).apply { + timeZone = TimeZone.getTimeZone("UTC") + } + val tz = TimeZone.getDefault() + + mapOf( + "datetime" to ToolValue.StringValue(dateFormatter.format(now)), + "time" to ToolValue.StringValue(timeFormatter.format(now)), + "timestamp" to ToolValue.StringValue(isoFormatter.format(now)), + "timezone" to ToolValue.StringValue(tz.id), + "utc_offset" to ToolValue.StringValue(tz.getDisplayName(false, TimeZone.SHORT)) + ) + } + ) + + // Calculator Tool - Math evaluation + RunAnywhereToolCalling.registerTool( + definition = ToolDefinition( + name = "calculate", + description = "Performs math calculations. Supports +, -, *, /, and parentheses", + parameters = listOf( + ToolParameter( + name = "expression", + type = ToolParameterType.STRING, + description = "Math expression (e.g., '2 + 2 * 3', '(10 + 5) / 3')", + required = true + ) + ), + category = "Utility" + ), + executor = { args: Map -> + val expression = (args["expression"] as? ToolValue.StringValue)?.value + ?: (args["input"] as? ToolValue.StringValue)?.value + ?: "0" + evaluateMathExpression(expression) + } + ) + + Log.i(TAG, "โœ… Demo tools registered") + refreshRegisteredTools() + + } catch (e: Exception) { + Log.e(TAG, "Failed to register demo tools", e) + } finally { + _uiState.update { it.copy(isLoading = false) } + } + } + } + + fun clearAllTools() { + viewModelScope.launch { + _uiState.update { it.copy(isLoading = true) } + try { + RunAnywhereToolCalling.clearTools() + refreshRegisteredTools() + Log.i(TAG, "โœ… All tools cleared") + } catch (e: Exception) { + Log.e(TAG, "Failed to clear tools", e) + } finally { + _uiState.update { it.copy(isLoading = false) } + } + } + } + + /** + * Detect the appropriate tool call format based on model name. + * LFM2-Tool models use the lfm2 format, others use default JSON format. + */ + fun detectToolCallFormat(modelName: String?): ToolCallFormat { + val name = modelName?.lowercase() ?: return ToolCallFormat.Default + return if (name.contains("lfm2") && name.contains("tool")) { + ToolCallFormat.LFM2 + } else { + ToolCallFormat.Default + } + } + + // ======================================================================== + // Tool Executor Implementations + // ======================================================================== + + /** + * Fetch weather using Open-Meteo API (free, no API key required) + * + * Uses a 15-second timeout for the entire operation (geocoding + weather fetch) + * to ensure tool execution respects LLM timeout settings. + */ + private suspend fun fetchWeather(location: String): Map { + return withContext(Dispatchers.IO) { + try { + // 15 second timeout for entire weather fetch operation + // This covers both geocoding and weather API calls + withTimeout(WEATHER_API_TIMEOUT_MS) { + // First, geocode the location + val geocodeUrl = "https://geocoding-api.open-meteo.com/v1/search?name=${URLEncoder.encode(location, "UTF-8")}&count=1" + val geocodeResponse = fetchUrl(geocodeUrl) + + // Parse geocode response (simple JSON parsing) + val latMatch = Regex("\"latitude\":\\s*(-?\\d+\\.?\\d*)").find(geocodeResponse) + val lonMatch = Regex("\"longitude\":\\s*(-?\\d+\\.?\\d*)").find(geocodeResponse) + val nameMatch = Regex("\"name\":\\s*\"([^\"]+)\"").find(geocodeResponse) + + if (latMatch == null || lonMatch == null) { + return@withTimeout mapOf( + "error" to ToolValue.StringValue("Location not found: $location"), + "location" to ToolValue.StringValue(location) + ) + } + + val lat = latMatch.groupValues[1] + val lon = lonMatch.groupValues[1] + val resolvedName = nameMatch?.groupValues?.get(1) ?: location + + // Fetch weather + val weatherUrl = "https://api.open-meteo.com/v1/forecast?latitude=$lat&longitude=$lon¤t=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m" + val weatherResponse = fetchUrl(weatherUrl) + + // Parse weather response + val tempMatch = Regex("\"temperature_2m\":\\s*(-?\\d+\\.?\\d*)").find(weatherResponse) + val humidityMatch = Regex("\"relative_humidity_2m\":\\s*(\\d+)").find(weatherResponse) + val windMatch = Regex("\"wind_speed_10m\":\\s*(-?\\d+\\.?\\d*)").find(weatherResponse) + val codeMatch = Regex("\"weather_code\":\\s*(\\d+)").find(weatherResponse) + + val temperature = tempMatch?.groupValues?.get(1)?.toDoubleOrNull() ?: 0.0 + val humidity = humidityMatch?.groupValues?.get(1)?.toIntOrNull() ?: 0 + val windSpeed = windMatch?.groupValues?.get(1)?.toDoubleOrNull() ?: 0.0 + val weatherCode = codeMatch?.groupValues?.get(1)?.toIntOrNull() ?: 0 + + val condition = when (weatherCode) { + 0 -> "Clear sky" + 1, 2, 3 -> "Partly cloudy" + 45, 48 -> "Foggy" + 51, 53, 55 -> "Drizzle" + 61, 63, 65 -> "Rain" + 71, 73, 75 -> "Snow" + 80, 81, 82 -> "Rain showers" + 95, 96, 99 -> "Thunderstorm" + else -> "Unknown" + } + + mapOf( + "location" to ToolValue.StringValue(resolvedName), + "temperature_celsius" to ToolValue.NumberValue(temperature), + "temperature_fahrenheit" to ToolValue.NumberValue(temperature * 9/5 + 32), + "humidity_percent" to ToolValue.NumberValue(humidity.toDouble()), + "wind_speed_kmh" to ToolValue.NumberValue(windSpeed), + "condition" to ToolValue.StringValue(condition) + ) + } + } catch (e: TimeoutCancellationException) { + Log.w(TAG, "Weather API request timed out for location: $location") + mapOf( + "error" to ToolValue.StringValue("Weather API request timed out. Please try again."), + "location" to ToolValue.StringValue(location) + ) + } catch (e: Exception) { + Log.e(TAG, "Weather fetch failed", e) + mapOf( + "error" to ToolValue.StringValue("Failed to fetch weather: ${e.message}"), + "location" to ToolValue.StringValue(location) + ) + } + } + } + + private fun fetchUrl(urlString: String): String { + val url = URL(urlString) + val connection = url.openConnection() as HttpURLConnection + connection.requestMethod = "GET" + connection.connectTimeout = 10000 + connection.readTimeout = 10000 + + return try { + connection.inputStream.bufferedReader().use { it.readText() } + } finally { + connection.disconnect() + } + } + + /** + * Evaluate a math expression + */ + private fun evaluateMathExpression(expression: String): Map { + return try { + // Clean the expression + val cleaned = expression + .replace("=", "") + .replace("x", "*") + .replace("ร—", "*") + .replace("รท", "/") + .trim() + + // Simple expression evaluator (handles basic math) + val result = evaluateSimpleExpression(cleaned) + + mapOf( + "result" to ToolValue.NumberValue(result), + "expression" to ToolValue.StringValue(expression) + ) + } catch (e: Exception) { + mapOf( + "error" to ToolValue.StringValue("Could not evaluate expression: $expression"), + "expression" to ToolValue.StringValue(expression) + ) + } + } + + /** + * Token parser with index-based iteration supporting peek operations. + * Enables lookahead for recursive descent parsing without consuming tokens. + */ + private class TokenParser(private val tokens: List) { + private var index = 0 + + /** Returns true if there are more tokens to consume */ + fun hasNext(): Boolean = index < tokens.size + + /** Returns and consumes the next token */ + fun next(): String { + if (!hasNext()) throw NoSuchElementException("No more tokens") + return tokens[index++] + } + + /** Returns the next token without consuming it, or null if no more tokens */ + fun peek(): String? = if (hasNext()) tokens[index] else null + } + + /** + * Simple recursive descent parser for math expressions + */ + private fun evaluateSimpleExpression(expr: String): Double { + val tokens = tokenize(expr) + val parser = TokenParser(tokens) + return parseExpression(parser) + } + + private fun tokenize(expr: String): List { + val tokens = mutableListOf() + var current = StringBuilder() + + for (char in expr) { + when { + char.isDigit() || char == '.' -> current.append(char) + char in "+-*/()" -> { + if (current.isNotEmpty()) { + tokens.add(current.toString()) + current = StringBuilder() + } + tokens.add(char.toString()) + } + char.isWhitespace() -> { + if (current.isNotEmpty()) { + tokens.add(current.toString()) + current = StringBuilder() + } + } + } + } + if (current.isNotEmpty()) { + tokens.add(current.toString()) + } + return tokens + } + + private fun parseExpression(parser: TokenParser): Double { + var left = parseTerm(parser) + while (parser.hasNext()) { + val op = parser.peek() ?: break + if (op != "+" && op != "-") break + parser.next() // consume the operator + val right = parseTerm(parser) + left = if (op == "+") left + right else left - right + } + return left + } + + private fun parseTerm(parser: TokenParser): Double { + var left = parseFactor(parser) + while (parser.hasNext()) { + val op = parser.peek() ?: break + if (op != "*" && op != "/") break + parser.next() // consume the operator + val right = parseFactor(parser) + left = if (op == "*") left * right else left / right + } + return left + } + + private fun parseFactor(parser: TokenParser): Double { + if (!parser.hasNext()) return 0.0 + val token = parser.next() + return when { + token == "(" -> { + val result = parseExpression(parser) + if (parser.hasNext()) parser.next() // consume ')' + result + } + token == "-" -> -parseFactor(parser) + else -> token.toDoubleOrNull() ?: 0.0 + } + } +} diff --git a/examples/flutter/RunAnywhereAI/ios/Runner.xcodeproj/project.pbxproj b/examples/flutter/RunAnywhereAI/ios/Runner.xcodeproj/project.pbxproj index 86437972e..dc8c54d29 100644 --- a/examples/flutter/RunAnywhereAI/ios/Runner.xcodeproj/project.pbxproj +++ b/examples/flutter/RunAnywhereAI/ios/Runner.xcodeproj/project.pbxproj @@ -470,7 +470,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = AFAL2647U9; + DEVELOPMENT_TEAM = L86FH3K93L; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -654,7 +654,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = AFAL2647U9; + DEVELOPMENT_TEAM = L86FH3K93L; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -678,7 +678,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = AFAL2647U9; + DEVELOPMENT_TEAM = L86FH3K93L; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/examples/flutter/RunAnywhereAI/lib/app/content_view.dart b/examples/flutter/RunAnywhereAI/lib/app/content_view.dart index 19b3dabe4..4c9065e4f 100644 --- a/examples/flutter/RunAnywhereAI/lib/app/content_view.dart +++ b/examples/flutter/RunAnywhereAI/lib/app/content_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:runanywhere_ai/core/design_system/app_colors.dart'; import 'package:runanywhere_ai/features/chat/chat_interface_view.dart'; import 'package:runanywhere_ai/features/settings/combined_settings_view.dart'; +import 'package:runanywhere_ai/features/tools/tools_view.dart'; import 'package:runanywhere_ai/features/voice/speech_to_text_view.dart'; import 'package:runanywhere_ai/features/voice/text_to_speech_view.dart'; import 'package:runanywhere_ai/features/voice/voice_assistant_view.dart'; @@ -26,7 +27,8 @@ class _ContentViewState extends State { SpeechToTextView(), // Tab 1: Speech-to-Text (Transcribe) TextToSpeechView(), // Tab 2: Text-to-Speech (Speak) VoiceAssistantView(), // Tab 3: Voice Assistant (STT + LLM + TTS) - CombinedSettingsView(), // Tab 4: Settings (includes Storage) + ToolsView(), // Tab 4: Tools (Tool Calling) + CombinedSettingsView(), // Tab 5: Settings (includes Storage) ]; @override @@ -66,6 +68,11 @@ class _ContentViewState extends State { selectedIcon: Icon(Icons.mic), label: 'Voice', ), + NavigationDestination( + icon: Icon(Icons.build_outlined), + selectedIcon: Icon(Icons.build), + label: 'Tools', + ), NavigationDestination( icon: Icon(Icons.settings_outlined), selectedIcon: Icon(Icons.settings), diff --git a/examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart b/examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart index aa10da675..2e714a0d0 100644 --- a/examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart +++ b/examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart @@ -189,7 +189,24 @@ class _RunAnywhereAIAppState extends State { 'https://huggingface.co/LiquidAI/LFM2-350M-GGUF/resolve/main/LFM2-350M-Q8_0.gguf', memoryRequirement: 400000000, ); - debugPrint('โœ… LlamaCPP module registered with LLM models'); + + // Tool Calling Optimized Models + // LFM2-1.2B-Tool - Designed for concise and precise tool calling (Liquid AI) + LlamaCpp.addModel( + id: 'lfm2-1.2b-tool-q4_k_m', + name: 'LiquidAI LFM2 1.2B Tool Q4_K_M', + url: + 'https://huggingface.co/LiquidAI/LFM2-1.2B-Tool-GGUF/resolve/main/LFM2-1.2B-Tool-Q4_K_M.gguf', + memoryRequirement: 800000000, + ); + LlamaCpp.addModel( + id: 'lfm2-1.2b-tool-q8_0', + name: 'LiquidAI LFM2 1.2B Tool Q8_0', + url: + 'https://huggingface.co/LiquidAI/LFM2-1.2B-Tool-GGUF/resolve/main/LFM2-1.2B-Tool-Q8_0.gguf', + memoryRequirement: 1400000000, + ); + debugPrint('โœ… LlamaCPP module registered with LLM models (including tool-calling optimized models)'); // Yield between module registrations await Future.delayed(Duration.zero); diff --git a/examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart b/examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart index d92b98f32..941c8ecff 100644 --- a/examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart +++ b/examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart @@ -1,16 +1,21 @@ import 'dart:async'; +import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:runanywhere/runanywhere.dart' as sdk; +import 'package:runanywhere/public/runanywhere_tool_calling.dart'; +import 'package:runanywhere/public/types/tool_calling_types.dart'; import 'package:runanywhere_ai/core/design_system/app_colors.dart'; import 'package:runanywhere_ai/core/design_system/app_spacing.dart'; import 'package:runanywhere_ai/core/design_system/typography.dart'; import 'package:runanywhere_ai/core/services/conversation_store.dart'; import 'package:runanywhere_ai/core/utilities/constants.dart'; +import 'package:runanywhere_ai/features/chat/tool_call_views.dart'; import 'package:runanywhere_ai/features/models/model_selection_sheet.dart'; import 'package:runanywhere_ai/features/models/model_status_components.dart'; import 'package:runanywhere_ai/features/models/model_types.dart'; +import 'package:runanywhere_ai/features/settings/tool_settings_view_model.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// ChatInterfaceView (mirroring iOS ChatInterfaceView.swift) @@ -117,16 +122,25 @@ class _ChatInterfaceViewState extends State { prefs.getDouble(PreferenceKeys.defaultTemperature) ?? 0.7; final maxTokens = prefs.getInt(PreferenceKeys.defaultMaxTokens) ?? 500; - // Streaming now runs in a background isolate, so no ANR concerns - final options = sdk.LLMGenerationOptions( - maxTokens: maxTokens, - temperature: temperature, - ); + // Check if tool calling is enabled and has registered tools + final toolSettings = ToolSettingsViewModel.shared; + final useToolCalling = toolSettings.toolCallingEnabled && + toolSettings.registeredTools.isNotEmpty; - if (_useStreaming) { - await _generateStreaming(userMessage, options); + if (useToolCalling) { + await _generateWithToolCalling(userMessage, maxTokens, temperature); } else { - await _generateNonStreaming(userMessage, options); + // Streaming now runs in a background isolate, so no ANR concerns + final options = sdk.LLMGenerationOptions( + maxTokens: maxTokens, + temperature: temperature, + ); + + if (_useStreaming) { + await _generateStreaming(userMessage, options); + } else { + await _generateNonStreaming(userMessage, options); + } } } catch (e) { setState(() { @@ -136,6 +150,147 @@ class _ChatInterfaceViewState extends State { } } + /// Determines the optimal tool calling format based on the model name/ID. + /// Different models are trained on different tool calling formats. + /// Returns format name string (C++ is single source of truth for valid formats). + String _detectToolCallFormat(String? modelName) { + if (modelName == null) return ToolCallFormatName.defaultFormat; + final name = modelName.toLowerCase(); + + // LFM2-Tool models use Pythonic format: <|tool_call_start|>[func(args)]<|tool_call_end|> + if (name.contains('lfm2') && name.contains('tool')) { + return ToolCallFormatName.lfm2; + } + + // Default JSON format for general-purpose models + return ToolCallFormatName.defaultFormat; + } + + Future _generateWithToolCalling( + String prompt, + int maxTokens, + double temperature, + ) async { + // Capture model name from local state (matches Swift pattern) + final modelName = _loadedModelName; + + // Auto-detect the tool calling format based on the loaded model + final format = _detectToolCallFormat(modelName); + debugPrint('Using tool calling with format: $format for model: ${modelName ?? "unknown"}'); + + // Add empty assistant message + final assistantMessage = ChatMessage( + id: DateTime.now().millisecondsSinceEpoch.toString(), + role: MessageRole.assistant, + content: '', + timestamp: DateTime.now(), + ); + + setState(() { + _messages.add(assistantMessage); + }); + + final messageIndex = _messages.length - 1; + + try { + final result = await RunAnywhereTools.generateWithTools( + prompt, + options: ToolCallingOptions( + maxToolCalls: 3, + autoExecute: true, + formatName: format, + maxTokens: maxTokens, + temperature: temperature, + ), + ); + + final totalTime = _generationStartTime != null + ? DateTime.now().difference(_generationStartTime!).inMilliseconds / + 1000.0 + : 0.0; + + // Create ToolCallInfo from the result if tools were called + ToolCallInfo? toolCallInfo; + debugPrint('๐Ÿ“Š Tool calling result: toolCalls=${result.toolCalls.length}, toolResults=${result.toolResults.length}'); + if (result.toolCalls.isNotEmpty) { + final lastCall = result.toolCalls.last; + final lastResult = result.toolResults.isNotEmpty + ? result.toolResults.last + : null; + debugPrint('๐Ÿ“Š Creating ToolCallInfo for: ${lastCall.toolName}'); + + toolCallInfo = ToolCallInfo( + toolName: lastCall.toolName, + arguments: _formatToolValueMapToJson(lastCall.arguments), + result: lastResult?.result != null + ? _formatToolValueMapToJson(lastResult!.result!) + : null, + success: lastResult?.success ?? false, + error: lastResult?.error, + ); + debugPrint('๐Ÿ“Š ToolCallInfo created: ${toolCallInfo.toolName}, success=${toolCallInfo.success}'); + } else { + debugPrint('๐Ÿ“Š No tool calls in result - badge will NOT show'); + } + + final analytics = MessageAnalytics( + messageId: assistantMessage.id, + modelName: modelName, + totalGenerationTime: totalTime, + ); + + if (!mounted) return; + setState(() { + _messages[messageIndex] = _messages[messageIndex].copyWith( + content: result.text, + analytics: analytics, + toolCallInfo: toolCallInfo, + ); + _isGenerating = false; + }); + + _scrollToBottom(); + } catch (e) { + if (!mounted) return; + setState(() { + _messages.removeLast(); + _errorMessage = 'Tool calling failed: $e'; + _isGenerating = false; + }); + } + } + + String _formatToolValueMapToJson(Map map) { + try { + final jsonMap = {}; + for (final entry in map.entries) { + jsonMap[entry.key] = _toolValueToJson(entry.value); + } + const encoder = JsonEncoder.withIndent(' '); + return encoder.convert(jsonMap); + } catch (e) { + return map.toString(); + } + } + + dynamic _toolValueToJson(ToolValue value) { + if (value is StringToolValue) return value.value; + if (value is NumberToolValue) return value.value; + if (value is BoolToolValue) return value.value; + if (value is NullToolValue) return null; + if (value is ArrayToolValue) { + return value.value.map((v) => _toolValueToJson(v)).toList(); + } + if (value is ObjectToolValue) { + final result = {}; + for (final entry in value.value.entries) { + result[entry.key] = _toolValueToJson(entry.value); + } + return result; + } + return value.toString(); + } + Future _generateStreaming( String prompt, sdk.LLMGenerationOptions options, @@ -455,6 +610,10 @@ class _ChatInterfaceViewState extends State { } Widget _buildInputArea() { + final toolSettings = ToolSettingsViewModel.shared; + final showToolBadge = toolSettings.toolCallingEnabled && + toolSettings.registeredTools.isNotEmpty; + return Container( padding: const EdgeInsets.all(AppSpacing.large), decoration: BoxDecoration( @@ -468,43 +627,54 @@ class _ChatInterfaceViewState extends State { ], ), child: SafeArea( - child: Row( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: TextField( - controller: _controller, - focusNode: _focusNode, - maxLines: 4, - minLines: 1, - textInputAction: TextInputAction.send, - decoration: InputDecoration( - hintText: 'Type a message...', - border: OutlineInputBorder( - borderRadius: - BorderRadius.circular(AppSpacing.cornerRadiusBubble), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: AppSpacing.large, - vertical: AppSpacing.mediumLarge, + // Tool calling badge (matches iOS) + if (showToolBadge) ...[ + ToolCallingBadge(toolCount: toolSettings.registeredTools.length), + const SizedBox(height: AppSpacing.smallMedium), + ], + Row( + children: [ + Expanded( + child: TextField( + controller: _controller, + focusNode: _focusNode, + maxLines: 4, + minLines: 1, + textInputAction: TextInputAction.send, + decoration: InputDecoration( + hintText: 'Type a message...', + border: OutlineInputBorder( + borderRadius: + BorderRadius.circular(AppSpacing.cornerRadiusBubble), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: AppSpacing.large, + vertical: AppSpacing.mediumLarge, + ), + ), + onSubmitted: (_) => _sendMessage(), + onChanged: (_) => setState(() {}), ), ), - onSubmitted: (_) => _sendMessage(), - onChanged: (_) => setState(() {}), - ), - ), - const SizedBox(width: AppSpacing.smallMedium), - IconButton.filled( - onPressed: _canSend ? _sendMessage : null, - icon: _isGenerating - ? const SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - color: Colors.white, - strokeWidth: 2, - ), - ) - : const Icon(Icons.arrow_upward), + const SizedBox(width: AppSpacing.smallMedium), + IconButton.filled( + onPressed: _canSend ? _sendMessage : null, + icon: _isGenerating + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + color: Colors.white, + strokeWidth: 2, + ), + ) + : const Icon(Icons.arrow_upward), + ), + ], ), ], ), @@ -524,6 +694,7 @@ class ChatMessage { final String? thinkingContent; final DateTime timestamp; final MessageAnalytics? analytics; + final ToolCallInfo? toolCallInfo; const ChatMessage({ required this.id, @@ -532,6 +703,7 @@ class ChatMessage { this.thinkingContent, required this.timestamp, this.analytics, + this.toolCallInfo, }); ChatMessage copyWith({ @@ -541,6 +713,7 @@ class ChatMessage { String? thinkingContent, DateTime? timestamp, MessageAnalytics? analytics, + ToolCallInfo? toolCallInfo, }) { return ChatMessage( id: id ?? this.id, @@ -549,6 +722,7 @@ class ChatMessage { thinkingContent: thinkingContent ?? this.thinkingContent, timestamp: timestamp ?? this.timestamp, analytics: analytics ?? this.analytics, + toolCallInfo: toolCallInfo ?? this.toolCallInfo, ); } } @@ -565,6 +739,7 @@ class _MessageBubble extends StatefulWidget { class _MessageBubbleState extends State<_MessageBubble> { bool _showThinking = false; + bool _showToolCallSheet = false; @override Widget build(BuildContext context) { @@ -581,6 +756,15 @@ class _MessageBubbleState extends State<_MessageBubble> { crossAxisAlignment: isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ + // Tool call indicator (if present, matches iOS toolCallSection) + if (widget.message.toolCallInfo != null && !isUser) ...[ + ToolCallIndicator( + toolCallInfo: widget.message.toolCallInfo!, + onTap: () => _showToolCallDetails(context), + ), + const SizedBox(height: AppSpacing.smallMedium), + ], + // Thinking section (if present) if (widget.message.thinkingContent != null && widget.message.thinkingContent!.isNotEmpty) @@ -638,6 +822,21 @@ class _MessageBubbleState extends State<_MessageBubble> { ); } + void _showToolCallDetails(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) => DraggableScrollableSheet( + initialChildSize: 0.6, + minChildSize: 0.3, + maxChildSize: 0.9, + builder: (context, scrollController) => + ToolCallDetailSheet(toolCallInfo: widget.message.toolCallInfo!), + ), + ); + } + Widget _buildThinkingSection() { return Container( margin: const EdgeInsets.only(bottom: AppSpacing.smallMedium), diff --git a/examples/flutter/RunAnywhereAI/lib/features/chat/tool_call_views.dart b/examples/flutter/RunAnywhereAI/lib/features/chat/tool_call_views.dart new file mode 100644 index 000000000..f856cc559 --- /dev/null +++ b/examples/flutter/RunAnywhereAI/lib/features/chat/tool_call_views.dart @@ -0,0 +1,384 @@ +import 'package:flutter/material.dart'; +import 'package:runanywhere_ai/core/design_system/app_colors.dart'; +import 'package:runanywhere_ai/core/design_system/app_spacing.dart'; +import 'package:runanywhere_ai/core/design_system/typography.dart'; + +/// Tool Call Info model (mirroring iOS ToolCallInfo) +class ToolCallInfo { + final String toolName; + final String arguments; // JSON string for display + final String? result; // JSON string for display + final bool success; + final String? error; + + const ToolCallInfo({ + required this.toolName, + required this.arguments, + this.result, + required this.success, + this.error, + }); +} + +/// Tool Call Indicator (mirroring iOS ToolCallIndicator) +/// +/// A tappable badge that shows tool call status and opens detail sheet. +class ToolCallIndicator extends StatelessWidget { + final ToolCallInfo toolCallInfo; + final VoidCallback onTap; + + const ToolCallIndicator({ + super.key, + required this.toolCallInfo, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + final isSuccess = toolCallInfo.success; + final accentColor = + isSuccess ? AppColors.primaryAccent : AppColors.primaryOrange; + + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + decoration: BoxDecoration( + color: accentColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: accentColor.withOpacity(0.3), + width: 0.5, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + isSuccess ? Icons.build_outlined : Icons.warning_amber, + size: 12, + color: accentColor, + ), + const SizedBox(width: 6), + Text( + toolCallInfo.toolName, + style: AppTypography.caption2(context).copyWith( + color: AppColors.textSecondary(context), + ), + ), + ], + ), + ), + ); + } +} + +/// Tool Call Detail Sheet (mirroring iOS ToolCallDetailSheet) +/// +/// Shows full details of a tool call. +class ToolCallDetailSheet extends StatelessWidget { + final ToolCallInfo toolCallInfo; + + const ToolCallDetailSheet({ + super.key, + required this.toolCallInfo, + }); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: AppColors.backgroundPrimary(context), + borderRadius: const BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Handle bar + Container( + width: 40, + height: 4, + margin: const EdgeInsets.only(top: 12), + decoration: BoxDecoration( + color: AppColors.separator(context), + borderRadius: BorderRadius.circular(2), + ), + ), + + // Header + Padding( + padding: const EdgeInsets.all(AppSpacing.large), + child: Row( + children: [ + Text( + 'Tool Call', + style: AppTypography.headline(context), + ), + const Spacer(), + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Done'), + ), + ], + ), + ), + + const Divider(height: 1), + + // Content + Flexible( + child: SingleChildScrollView( + padding: const EdgeInsets.all(AppSpacing.large), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Status section + _buildStatusSection(context), + + const SizedBox(height: AppSpacing.xLarge), + + // Tool name + _buildDetailSection( + context, + title: 'Tool', + content: toolCallInfo.toolName, + ), + + const SizedBox(height: AppSpacing.xLarge), + + // Arguments + _buildCodeSection( + context, + title: 'Arguments', + code: toolCallInfo.arguments, + ), + + // Result (if present) + if (toolCallInfo.result != null) ...[ + const SizedBox(height: AppSpacing.xLarge), + _buildCodeSection( + context, + title: 'Result', + code: toolCallInfo.result!, + ), + ], + + // Error (if present) + if (toolCallInfo.error != null) ...[ + const SizedBox(height: AppSpacing.xLarge), + _buildDetailSection( + context, + title: 'Error', + content: toolCallInfo.error!, + isError: true, + ), + ], + + const SizedBox(height: AppSpacing.xxLarge), + ], + ), + ), + ), + ], + ), + ); + } + + Widget _buildStatusSection(BuildContext context) { + final isSuccess = toolCallInfo.success; + final statusColor = + isSuccess ? AppColors.statusGreen : AppColors.primaryRed; + + return Container( + padding: const EdgeInsets.all(AppSpacing.large), + decoration: BoxDecoration( + color: statusColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Icon( + isSuccess ? Icons.check_circle : Icons.cancel, + size: 24, + color: statusColor, + ), + const SizedBox(width: 10), + Text( + isSuccess ? 'Success' : 'Failed', + style: AppTypography.headline(context), + ), + ], + ), + ); + } + + Widget _buildDetailSection( + BuildContext context, { + required String title, + required String content, + bool isError = false, + }) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: AppTypography.caption(context).copyWith( + color: AppColors.textSecondary(context), + ), + ), + const SizedBox(height: 8), + Text( + content, + style: AppTypography.body(context).copyWith( + color: isError ? AppColors.primaryRed : null, + ), + ), + ], + ); + } + + Widget _buildCodeSection( + BuildContext context, { + required String title, + required String code, + }) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: AppTypography.caption(context).copyWith( + color: AppColors.textSecondary(context), + ), + ), + const SizedBox(height: 8), + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: AppColors.backgroundSecondary(context), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + code, + style: AppTypography.monospaced, + ), + ), + ], + ); + } +} + +/// Tool Calling Active Indicator (mirroring iOS ToolCallingActiveIndicator) +/// +/// Shows "Calling tool..." with animated gear icon. +class ToolCallingActiveIndicator extends StatefulWidget { + const ToolCallingActiveIndicator({super.key}); + + @override + State createState() => + _ToolCallingActiveIndicatorState(); +} + +class _ToolCallingActiveIndicatorState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(seconds: 2), + vsync: this, + )..repeat(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + decoration: BoxDecoration( + color: AppColors.primaryAccent.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RotationTransition( + turns: _controller, + child: const Icon( + Icons.settings, + size: 12, + color: AppColors.primaryBlue, + ), + ), + const SizedBox(width: 6), + Text( + 'Calling tool...', + style: AppTypography.caption2(context).copyWith( + color: AppColors.textSecondary(context), + ), + ), + ], + ), + ); + } +} + +/// Tool Calling Badge (mirroring iOS toolCallingBadge) +/// +/// Shows "Tools enabled" badge above chat input. +class ToolCallingBadge extends StatelessWidget { + final int toolCount; + + const ToolCallingBadge({ + super.key, + required this.toolCount, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 4, + ), + decoration: BoxDecoration( + color: AppColors.primaryAccent.withOpacity(0.1), + borderRadius: BorderRadius.circular(6), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.build_outlined, + size: 10, + color: AppColors.primaryBlue, + ), + const SizedBox(width: 6), + Text( + 'Tools enabled ($toolCount)', + style: AppTypography.caption2(context).copyWith( + color: AppColors.primaryAccent, + ), + ), + ], + ), + ); + } +} diff --git a/examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart b/examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart index 388059db6..7f34ff25d 100644 --- a/examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart +++ b/examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart @@ -2,12 +2,14 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:runanywhere/runanywhere.dart' as sdk; +import 'package:runanywhere/public/types/tool_calling_types.dart'; import 'package:runanywhere_ai/core/design_system/app_colors.dart'; import 'package:runanywhere_ai/core/design_system/app_spacing.dart'; import 'package:runanywhere_ai/core/design_system/typography.dart'; import 'package:runanywhere_ai/core/models/app_types.dart'; import 'package:runanywhere_ai/core/utilities/constants.dart'; import 'package:runanywhere_ai/core/utilities/keychain_helper.dart'; +import 'package:runanywhere_ai/features/settings/tool_settings_view_model.dart'; import 'package:url_launcher/url_launcher.dart'; /// CombinedSettingsView (mirroring iOS CombinedSettingsView.swift) @@ -381,6 +383,11 @@ class _CombinedSettingsViewState extends State { body: ListView( padding: const EdgeInsets.all(AppSpacing.large), children: [ + // Tool Calling Section (matches iOS) + _buildSectionHeader('Tool Calling'), + _buildToolCallingCard(), + const SizedBox(height: AppSpacing.large), + // API Configuration Section _buildSectionHeader('API Configuration (Testing)'), _buildApiConfigurationCard(), @@ -449,6 +456,96 @@ class _CombinedSettingsViewState extends State { ); } + Widget _buildToolCallingCard() { + return ListenableBuilder( + listenable: ToolSettingsViewModel.shared, + builder: (context, _) { + final viewModel = ToolSettingsViewModel.shared; + return Card( + child: Padding( + padding: const EdgeInsets.all(AppSpacing.large), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Enable toggle + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('Enable Tool Calling'), + subtitle: const Text( + 'Allow the LLM to use registered tools', + ), + value: viewModel.toolCallingEnabled, + onChanged: (value) { + viewModel.toolCallingEnabled = value; + }, + ), + + if (viewModel.toolCallingEnabled) ...[ + const Divider(), + + // Registered tools count + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Registered Tools', + style: AppTypography.subheadline(context), + ), + Text( + '${viewModel.registeredTools.length}', + style: AppTypography.subheadlineSemibold(context) + .copyWith( + color: AppColors.primaryAccent, + ), + ), + ], + ), + + const SizedBox(height: AppSpacing.mediumLarge), + + // Add/Clear tools buttons + if (viewModel.registeredTools.isEmpty) + OutlinedButton.icon( + onPressed: () async { + await viewModel.registerDemoTools(); + }, + icon: const Icon(Icons.add), + label: const Text('Add Demo Tools'), + ) + else ...[ + // Show registered tools + ...viewModel.registeredTools.map( + (tool) => _ToolRow(tool: tool), + ), + const SizedBox(height: AppSpacing.mediumLarge), + OutlinedButton.icon( + onPressed: () async { + await viewModel.clearAllTools(); + }, + icon: const Icon(Icons.delete_outline), + label: const Text('Clear All Tools'), + style: OutlinedButton.styleFrom( + foregroundColor: AppColors.primaryRed, + ), + ), + ], + ], + + const SizedBox(height: AppSpacing.mediumLarge), + Text( + 'Allow the LLM to use registered tools to perform actions like getting weather, time, or calculations.', + style: AppTypography.caption(context).copyWith( + color: AppColors.textSecondary(context), + ), + ), + ], + ), + ), + ); + }, + ); + } + Widget _buildApiConfigurationCard() { return Card( child: Padding( @@ -898,3 +995,76 @@ class _StoredModelRowState extends State<_StoredModelRow> { } } } + +/// Tool row widget (mirroring iOS ToolRow) +class _ToolRow extends StatelessWidget { + final ToolDefinition tool; + + const _ToolRow({required this.tool}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: AppSpacing.xSmall), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.build_outlined, + size: 12, + color: AppColors.primaryAccent, + ), + const SizedBox(width: 8), + Text( + tool.name, + style: AppTypography.subheadlineSemibold(context), + ), + ], + ), + const SizedBox(height: 4), + Text( + tool.description, + style: AppTypography.caption(context).copyWith( + color: AppColors.textSecondary(context), + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + if (tool.parameters.isNotEmpty) ...[ + const SizedBox(height: 4), + Wrap( + spacing: 4, + runSpacing: 4, + children: [ + Text( + 'Params:', + style: AppTypography.caption2(context).copyWith( + color: AppColors.textSecondary(context), + ), + ), + ...tool.parameters.map( + (param) => Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, + vertical: 2, + ), + decoration: BoxDecoration( + color: AppColors.backgroundTertiary(context), + borderRadius: BorderRadius.circular(4), + ), + child: Text( + param.name, + style: AppTypography.caption2(context), + ), + ), + ), + ], + ), + ], + ], + ), + ); + } +} diff --git a/examples/flutter/RunAnywhereAI/lib/features/settings/tool_settings_view_model.dart b/examples/flutter/RunAnywhereAI/lib/features/settings/tool_settings_view_model.dart new file mode 100644 index 000000000..0660ca47b --- /dev/null +++ b/examples/flutter/RunAnywhereAI/lib/features/settings/tool_settings_view_model.dart @@ -0,0 +1,376 @@ +import 'dart:async'; +import 'dart:math' as math; + +import 'package:flutter/foundation.dart'; +import 'package:runanywhere/public/runanywhere_tool_calling.dart'; +import 'package:runanywhere/public/types/tool_calling_types.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:shared_preferences/shared_preferences.dart'; + +/// Tool Settings ViewModel (mirroring iOS ToolSettingsViewModel) +/// +/// Manages tool calling state and registered tools. +class ToolSettingsViewModel extends ChangeNotifier { + // Singleton pattern (matches iOS) + static final ToolSettingsViewModel shared = ToolSettingsViewModel._internal(); + + factory ToolSettingsViewModel() => shared; + + ToolSettingsViewModel._internal() { + unawaited(_loadSettings()); + } + + // State + List _registeredTools = []; + bool _toolCallingEnabled = false; + + // Getters + List get registeredTools => _registeredTools; + bool get toolCallingEnabled => _toolCallingEnabled; + + set toolCallingEnabled(bool value) { + _toolCallingEnabled = value; + unawaited(_saveSettings()); + notifyListeners(); + } + + Future _loadSettings() async { + final prefs = await SharedPreferences.getInstance(); + _toolCallingEnabled = prefs.getBool('tool_calling_enabled') ?? false; + await refreshRegisteredTools(); + notifyListeners(); + } + + Future _saveSettings() async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool('tool_calling_enabled', _toolCallingEnabled); + } + + Future refreshRegisteredTools() async { + _registeredTools = RunAnywhereTools.getRegisteredTools(); + notifyListeners(); + } + + /// Register demo tools (matches iOS implementation) + Future registerDemoTools() async { + // 1. Weather Tool - Uses Open-Meteo API (free, no API key required) + RunAnywhereTools.registerTool( + const ToolDefinition( + name: 'get_weather', + description: + 'Gets the current weather for a given location using Open-Meteo API', + parameters: [ + ToolParameter( + name: 'location', + type: ToolParameterType.string, + description: "City name (e.g., 'San Francisco', 'London', 'Tokyo')", + ), + ], + ), + _fetchWeather, + ); + + // 2. Time Tool - Real system time with timezone + RunAnywhereTools.registerTool( + const ToolDefinition( + name: 'get_current_time', + description: 'Gets the current date, time, and timezone information', + parameters: [], + ), + _getCurrentTime, + ); + + // 3. Calculator Tool - Real math evaluation + RunAnywhereTools.registerTool( + const ToolDefinition( + name: 'calculate', + description: + 'Performs math calculations. Supports +, -, *, /, and parentheses', + parameters: [ + ToolParameter( + name: 'expression', + type: ToolParameterType.string, + description: "Math expression (e.g., '2 + 2 * 3', '(10 + 5) / 3')", + ), + ], + ), + _calculate, + ); + + await refreshRegisteredTools(); + } + + Future clearAllTools() async { + RunAnywhereTools.clearTools(); + await refreshRegisteredTools(); + } + + // MARK: - Tool Executors + + /// Weather tool executor - fetches real weather data from Open-Meteo + Future> _fetchWeather( + Map args, + ) async { + final rawLocation = args['location']?.stringValue; + + // Require location argument - no hardcoded defaults + if (rawLocation == null || rawLocation.isEmpty) { + return { + 'error': const StringToolValue('Missing required argument: location'), + }; + } + + // Clean up location string - Open-Meteo works better with just city names + // Remove common suffixes like ", CA", ", US", ", USA", etc. + final location = _cleanLocationString(rawLocation); + + try { + // Step 1: Geocode the location + final geocodeUrl = Uri.parse( + 'https://geocoding-api.open-meteo.com/v1/search?name=${Uri.encodeComponent(location)}&count=5&language=en&format=json', + ); + + final geocodeResponse = await http.get(geocodeUrl); + if (geocodeResponse.statusCode != 200) { + throw Exception('Geocoding failed'); + } + + final geocodeData = + jsonDecode(geocodeResponse.body) as Map; + final results = geocodeData['results'] as List?; + if (results == null || results.isEmpty) { + return { + 'error': StringToolValue('Could not find location: $location'), + 'location': StringToolValue(location), + }; + } + + final firstResult = results[0] as Map; + final lat = firstResult['latitude'] as num; + final lon = firstResult['longitude'] as num; + final cityName = firstResult['name'] as String? ?? location; + + // Step 2: Fetch weather for coordinates + final weatherUrl = Uri.parse( + 'https://api.open-meteo.com/v1/forecast?latitude=$lat&longitude=$lon¤t=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m&temperature_unit=fahrenheit&wind_speed_unit=mph', + ); + + final weatherResponse = await http.get(weatherUrl); + if (weatherResponse.statusCode != 200) { + throw Exception('Weather fetch failed'); + } + + final weatherData = + jsonDecode(weatherResponse.body) as Map; + final current = weatherData['current'] as Map; + final temp = current['temperature_2m'] as num? ?? 0; + final humidity = current['relative_humidity_2m'] as num? ?? 0; + final windSpeed = current['wind_speed_10m'] as num? ?? 0; + final weatherCode = current['weather_code'] as int? ?? 0; + + return { + 'location': StringToolValue(cityName), + 'temperature': NumberToolValue(temp.toDouble()), + 'unit': const StringToolValue('fahrenheit'), + 'humidity': NumberToolValue(humidity.toDouble()), + 'wind_speed_mph': NumberToolValue(windSpeed.toDouble()), + 'condition': StringToolValue(_weatherCodeToCondition(weatherCode)), + }; + } catch (e) { + return { + 'error': StringToolValue('Weather fetch failed: $e'), + 'location': StringToolValue(location), + }; + } + } + + String _weatherCodeToCondition(int code) { + switch (code) { + case 0: + return 'Clear sky'; + case 1: + return 'Mainly clear'; + case 2: + return 'Partly cloudy'; + case 3: + return 'Overcast'; + case 45: + case 48: + return 'Foggy'; + case 51: + case 53: + case 55: + return 'Drizzle'; + case 56: + case 57: + return 'Freezing drizzle'; + case 61: + case 63: + case 65: + return 'Rain'; + case 66: + case 67: + return 'Freezing rain'; + case 71: + case 73: + case 75: + return 'Snow'; + case 77: + return 'Snow grains'; + case 80: + case 81: + case 82: + return 'Rain showers'; + case 85: + case 86: + return 'Snow showers'; + case 95: + return 'Thunderstorm'; + case 96: + case 99: + return 'Thunderstorm with hail'; + default: + return 'Unknown'; + } + } + + /// Clean location string for better geocoding results + /// Removes common suffixes like ", CA", ", US", state abbreviations, etc. + String _cleanLocationString(String location) { + var cleaned = location.trim(); + + // Common patterns to remove: ", CA", ", NY", ", US", ", USA", ", United States" + // Also handle variations like "CA" at the end + final patterns = [ + RegExp(r',\s*(US|USA|United States)$', caseSensitive: false), + RegExp(r',\s*[A-Z]{2}$'), // State abbreviations like ", CA", ", NY" + RegExp(r',\s*[A-Z]{2},\s*(US|USA)$', caseSensitive: false), // ", CA, US" + ]; + + for (final pattern in patterns) { + cleaned = cleaned.replaceAll(pattern, ''); + } + + // Also handle "SF" -> "San Francisco" for common abbreviations + final abbreviations = { + 'SF': 'San Francisco', + 'NYC': 'New York City', + 'LA': 'Los Angeles', + 'DC': 'Washington DC', + }; + + final upperCleaned = cleaned.toUpperCase(); + if (abbreviations.containsKey(upperCleaned)) { + return abbreviations[upperCleaned]!; + } + + return cleaned; + } + + /// Time tool executor + Future> _getCurrentTime( + Map args, + ) async { + final now = DateTime.now(); + + return { + 'datetime': StringToolValue(now.toString()), + 'time': StringToolValue( + '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}', + ), + 'timestamp': StringToolValue(now.toIso8601String()), + 'timezone': StringToolValue(now.timeZoneName), + 'utc_offset': StringToolValue( + '${now.timeZoneOffset.isNegative ? '-' : '+'}${now.timeZoneOffset.inHours.abs().toString().padLeft(2, '0')}:${(now.timeZoneOffset.inMinutes.abs() % 60).toString().padLeft(2, '0')}', + ), + }; + } + + /// Calculator tool executor + Future> _calculate( + Map args, + ) async { + // Try both 'expression' and 'input' keys - no hardcoded defaults + final expression = args['expression']?.stringValue ?? args['input']?.stringValue; + + if (expression == null || expression.isEmpty) { + return { + 'error': const StringToolValue('Missing required argument: expression'), + }; + } + + try { + // Clean the expression + final cleanedExpression = expression + .replaceAll('=', '') + .replaceAll('x', '*') + .replaceAll('ร—', '*') + .replaceAll('รท', '/') + .trim(); + + final result = _evaluateExpression(cleanedExpression); + + return { + 'result': NumberToolValue(result), + 'expression': StringToolValue(expression), + }; + } catch (e) { + return { + 'error': StringToolValue('Could not evaluate expression: $expression'), + 'expression': StringToolValue(expression), + }; + } + } + + double _evaluateExpression(String expr) { + // Remove spaces + expr = expr.replaceAll(' ', ''); + + // Handle power operator first (** or ^) + if (expr.contains('**')) { + final parts = expr.split('**'); + var result = double.parse(parts[0]); + for (var i = 1; i < parts.length; i++) { + result = _pow(result, double.parse(parts[i])); + } + return result; + } else if (expr.contains('^')) { + final parts = expr.split('^'); + var result = double.parse(parts[0]); + for (var i = 1; i < parts.length; i++) { + result = _pow(result, double.parse(parts[i])); + } + return result; + } + + // Handle simple operations + if (expr.contains('+')) { + final parts = expr.split('+'); + return parts.map((p) => double.parse(p)).reduce((a, b) => a + b); + } else if (expr.contains('-') && !expr.startsWith('-')) { + final parts = expr.split('-'); + var result = double.parse(parts[0]); + for (var i = 1; i < parts.length; i++) { + result -= double.parse(parts[i]); + } + return result; + } else if (expr.contains('*')) { + final parts = expr.split('*'); + return parts.map((p) => double.parse(p)).reduce((a, b) => a * b); + } else if (expr.contains('/')) { + final parts = expr.split('/'); + var result = double.parse(parts[0]); + for (var i = 1; i < parts.length; i++) { + result /= double.parse(parts[i]); + } + return result; + } + + return double.parse(expr); + } + + double _pow(double base, double exponent) { + return math.pow(base, exponent).toDouble(); + } +} diff --git a/examples/flutter/RunAnywhereAI/lib/features/tools/tools_view.dart b/examples/flutter/RunAnywhereAI/lib/features/tools/tools_view.dart new file mode 100644 index 000000000..cf2d23875 --- /dev/null +++ b/examples/flutter/RunAnywhereAI/lib/features/tools/tools_view.dart @@ -0,0 +1,782 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:math' as math; + +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:runanywhere/runanywhere.dart' as sdk; +import 'package:runanywhere/public/runanywhere_tool_calling.dart'; +import 'package:runanywhere/public/types/tool_calling_types.dart'; +import 'package:runanywhere_ai/core/design_system/app_colors.dart'; +import 'package:runanywhere_ai/core/design_system/app_spacing.dart'; +import 'package:runanywhere_ai/core/design_system/typography.dart'; +import 'package:runanywhere_ai/features/models/model_selection_sheet.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +/// ToolsView - Demonstrates tool calling functionality +/// +/// Matches iOS ToolsScreen and Android ToolSettingsScreen +class ToolsView extends StatefulWidget { + const ToolsView({super.key}); + + @override + State createState() => _ToolsViewState(); +} + +class _ToolsViewState extends State { + final TextEditingController _promptController = TextEditingController(); + final ScrollController _scrollController = ScrollController(); + + // State + bool _toolCallingEnabled = true; + bool _isGenerating = false; + String? _errorMessage; + List _registeredTools = []; + + // Results + String _toolExecutionLog = ''; + String _finalResponse = ''; + + // Model state + String? _loadedModelName; + + @override + void initState() { + super.initState(); + unawaited(_loadSettings()); + unawaited(_syncModelState()); + _registerDemoTools(); + } + + @override + void dispose() { + _promptController.dispose(); + _scrollController.dispose(); + super.dispose(); + } + + Future _loadSettings() async { + final prefs = await SharedPreferences.getInstance(); + setState(() { + _toolCallingEnabled = prefs.getBool('tool_calling_enabled') ?? true; + }); + } + + Future _saveSettings() async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool('tool_calling_enabled', _toolCallingEnabled); + } + + Future _syncModelState() async { + final model = await sdk.RunAnywhere.currentLLMModel(); + if (mounted) { + setState(() { + _loadedModelName = model?.name; + }); + } + } + + /// Register demo tools matching iOS/Android examples + void _registerDemoTools() { + // Clear any existing tools + RunAnywhereTools.clearTools(); + + // 1. Weather tool + RunAnywhereTools.registerTool( + const ToolDefinition( + name: 'get_weather', + description: 'Get current weather for a location', + parameters: [ + ToolParameter( + name: 'location', + type: ToolParameterType.string, + description: 'City name or coordinates (e.g., "San Francisco, CA")', + ), + ], + ), + _fetchWeather, + ); + + // 2. Calculator tool + RunAnywhereTools.registerTool( + const ToolDefinition( + name: 'calculate', + description: 'Perform basic arithmetic calculations', + parameters: [ + ToolParameter( + name: 'expression', + type: ToolParameterType.string, + description: 'Math expression (e.g., "2 + 2", "10 * 5")', + ), + ], + ), + _calculate, + ); + + // 3. Time tool + RunAnywhereTools.registerTool( + const ToolDefinition( + name: 'get_current_time', + description: 'Get the current date and time', + parameters: [], + ), + _getCurrentTime, + ); + + setState(() { + _registeredTools = RunAnywhereTools.getRegisteredTools(); + }); + } + + /// Weather tool executor - fetches real weather data + Future> _fetchWeather( + Map args, + ) async { + final rawLocation = args['location']?.stringValue; + + // Require location argument - no hardcoded defaults + if (rawLocation == null || rawLocation.isEmpty) { + return { + 'error': const StringToolValue('Missing required argument: location'), + }; + } + + // Clean up location string for better geocoding + final location = _cleanLocationString(rawLocation); + + try { + // Use Open-Meteo API (no key required) + final geocodeUrl = Uri.parse( + 'https://geocoding-api.open-meteo.com/v1/search?name=${Uri.encodeComponent(location)}&count=5&language=en&format=json', + ); + + final geocodeResponse = await http.get(geocodeUrl); + if (geocodeResponse.statusCode != 200) { + throw Exception('Geocoding failed'); + } + + final geocodeData = jsonDecode(geocodeResponse.body) as Map; + final results = geocodeData['results'] as List?; + if (results == null || results.isEmpty) { + return { + 'error': StringToolValue('Could not find location: $location'), + 'location': StringToolValue(location), + }; + } + + final firstResult = results[0] as Map; + final lat = firstResult['latitude'] as num; + final lon = firstResult['longitude'] as num; + final cityName = firstResult['name'] as String? ?? location; + + // Fetch weather + final weatherUrl = Uri.parse( + 'https://api.open-meteo.com/v1/forecast?latitude=$lat&longitude=$lon¤t=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m&temperature_unit=fahrenheit&wind_speed_unit=mph', + ); + + final weatherResponse = await http.get(weatherUrl); + if (weatherResponse.statusCode != 200) { + throw Exception('Weather fetch failed'); + } + + final weatherData = jsonDecode(weatherResponse.body) as Map; + final current = weatherData['current'] as Map; + final temp = current['temperature_2m'] as num? ?? 0; + final humidity = current['relative_humidity_2m'] as num? ?? 0; + final windSpeed = current['wind_speed_10m'] as num? ?? 0; + final weatherCode = current['weather_code'] as int? ?? 0; + + return { + 'location': StringToolValue(cityName), + 'temperature': NumberToolValue(temp.toDouble()), + 'unit': const StringToolValue('fahrenheit'), + 'humidity': NumberToolValue(humidity.toDouble()), + 'wind_speed_mph': NumberToolValue(windSpeed.toDouble()), + 'condition': StringToolValue(_weatherCodeToCondition(weatherCode)), + }; + } catch (e) { + return { + 'error': StringToolValue('Weather fetch failed: $e'), + 'location': StringToolValue(location), + }; + } + } + + /// Clean location string for better geocoding results + String _cleanLocationString(String location) { + var cleaned = location.trim(); + + // Common patterns to remove: ", CA", ", NY", ", US", ", USA" + final patterns = [ + RegExp(r',\s*(US|USA|United States)$', caseSensitive: false), + RegExp(r',\s*[A-Z]{2}$'), // State abbreviations like ", CA", ", NY" + RegExp(r',\s*[A-Z]{2},\s*(US|USA)$', caseSensitive: false), + ]; + + for (final pattern in patterns) { + cleaned = cleaned.replaceAll(pattern, ''); + } + + // Handle common abbreviations + final abbreviations = { + 'SF': 'San Francisco', + 'NYC': 'New York City', + 'LA': 'Los Angeles', + 'DC': 'Washington DC', + }; + + final upperCleaned = cleaned.toUpperCase(); + if (abbreviations.containsKey(upperCleaned)) { + return abbreviations[upperCleaned]!; + } + + return cleaned; + } + + String _weatherCodeToCondition(int code) { + switch (code) { + case 0: + return 'Clear sky'; + case 1: + return 'Mainly clear'; + case 2: + return 'Partly cloudy'; + case 3: + return 'Overcast'; + case 45: + case 48: + return 'Foggy'; + case 51: + case 53: + case 55: + return 'Drizzle'; + case 56: + case 57: + return 'Freezing drizzle'; + case 61: + case 63: + case 65: + return 'Rain'; + case 66: + case 67: + return 'Freezing rain'; + case 71: + case 73: + case 75: + return 'Snow'; + case 77: + return 'Snow grains'; + case 80: + case 81: + case 82: + return 'Rain showers'; + case 85: + case 86: + return 'Snow showers'; + case 95: + return 'Thunderstorm'; + case 96: + case 99: + return 'Thunderstorm with hail'; + default: + return 'Unknown'; + } + } + + /// Calculator tool executor + Future> _calculate( + Map args, + ) async { + final expression = args['expression']?.stringValue; + + // Require expression argument - no hardcoded defaults + if (expression == null || expression.isEmpty) { + return { + 'error': const StringToolValue('Missing required argument: expression'), + }; + } + + try { + // Simple expression parser for basic arithmetic + final result = _evaluateExpression(expression); + return { + 'expression': StringToolValue(expression), + 'result': NumberToolValue(result), + }; + } catch (e) { + return { + 'error': StringToolValue('Calculation failed: $e'), + 'expression': StringToolValue(expression), + }; + } + } + + double _evaluateExpression(String expr) { + // Remove spaces + expr = expr.replaceAll(' ', ''); + + // Handle power operator first (** or ^) + if (expr.contains('**')) { + final parts = expr.split('**'); + var result = double.parse(parts[0]); + for (var i = 1; i < parts.length; i++) { + result = _pow(result, double.parse(parts[i])); + } + return result; + } else if (expr.contains('^')) { + final parts = expr.split('^'); + var result = double.parse(parts[0]); + for (var i = 1; i < parts.length; i++) { + result = _pow(result, double.parse(parts[i])); + } + return result; + } + + // Handle simple operations + if (expr.contains('+')) { + final parts = expr.split('+'); + return parts.map((p) => double.parse(p)).reduce((a, b) => a + b); + } else if (expr.contains('-')) { + final parts = expr.split('-'); + var result = double.parse(parts[0]); + for (var i = 1; i < parts.length; i++) { + result -= double.parse(parts[i]); + } + return result; + } else if (expr.contains('*')) { + final parts = expr.split('*'); + return parts.map((p) => double.parse(p)).reduce((a, b) => a * b); + } else if (expr.contains('/')) { + final parts = expr.split('/'); + var result = double.parse(parts[0]); + for (var i = 1; i < parts.length; i++) { + result /= double.parse(parts[i]); + } + return result; + } + + return double.parse(expr); + } + + double _pow(double base, double exponent) { + return math.pow(base, exponent).toDouble(); + } + + /// Time tool executor + Future> _getCurrentTime( + Map args, + ) async { + final now = DateTime.now(); + return { + 'date': StringToolValue( + '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}', + ), + 'time': StringToolValue( + '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}', + ), + 'timezone': StringToolValue(now.timeZoneName), + }; + } + + Future _runToolCalling() async { + if (!sdk.RunAnywhere.isModelLoaded) { + setState(() { + _errorMessage = 'Please load an LLM model first'; + }); + return; + } + + final prompt = _promptController.text.trim(); + if (prompt.isEmpty) { + setState(() { + _errorMessage = 'Please enter a prompt'; + }); + return; + } + + setState(() { + _isGenerating = true; + _errorMessage = null; + _toolExecutionLog = ''; + _finalResponse = ''; + }); + + try { + _addToLog('Starting generation with tools...'); + + final result = await RunAnywhereTools.generateWithTools( + prompt, + options: const ToolCallingOptions( + maxToolCalls: 3, + autoExecute: true, + ), + ); + + // Log tool calls + for (final toolCall in result.toolCalls) { + _addToLog('Tool called: ${toolCall.toolName}'); + _addToLog('Arguments: ${toolCall.arguments}'); + } + + // Log tool results + for (final toolResult in result.toolResults) { + _addToLog('Tool result: ${toolResult.toolName}'); + _addToLog('Success: ${toolResult.success}'); + if (toolResult.result != null) { + _addToLog('Result: ${toolResult.result}'); + } + if (toolResult.error != null) { + _addToLog('Error: ${toolResult.error}'); + } + } + + setState(() { + _finalResponse = result.text; + _isGenerating = false; + }); + } catch (e) { + setState(() { + _errorMessage = 'Tool calling failed: $e'; + _isGenerating = false; + }); + } + } + + void _addToLog(String message) { + setState(() { + final timestamp = DateTime.now().toString().substring(11, 19); + _toolExecutionLog += '[$timestamp] $message\n'; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.backgroundPrimary(context), + body: SafeArea( + child: Column( + children: [ + // Model status header + _buildHeader(context), + + // Main content + Expanded( + child: SingleChildScrollView( + controller: _scrollController, + padding: const EdgeInsets.all(AppSpacing.padding16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Tool calling toggle + _buildToolCallingToggle(context), + + const SizedBox(height: AppSpacing.large), + + // Registered tools + _buildRegisteredToolsSection(context), + + const SizedBox(height: AppSpacing.large), + + // Prompt input + _buildPromptInput(context), + + const SizedBox(height: AppSpacing.medium), + + // Run button + _buildRunButton(context), + + // Error message + if (_errorMessage != null) ...[ + const SizedBox(height: AppSpacing.medium), + _buildErrorMessage(context), + ], + + // Results + if (_toolExecutionLog.isNotEmpty) ...[ + const SizedBox(height: AppSpacing.large), + _buildExecutionLog(context), + ], + + if (_finalResponse.isNotEmpty) ...[ + const SizedBox(height: AppSpacing.large), + _buildFinalResponse(context), + ], + ], + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildHeader(BuildContext context) { + return Container( + padding: const EdgeInsets.all(AppSpacing.padding16), + decoration: BoxDecoration( + color: AppColors.backgroundSecondary(context), + border: Border( + bottom: BorderSide(color: AppColors.separator(context)), + ), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Tool Calling', + style: AppTypography.headline(context), + ), + const SizedBox(height: 4), + if (_loadedModelName != null) + Text( + 'Model: $_loadedModelName', + style: AppTypography.caption(context).copyWith( + color: AppColors.textSecondary(context), + ), + ) + else + Text( + 'No model loaded', + style: AppTypography.caption(context).copyWith( + color: AppColors.primaryOrange, + ), + ), + ], + ), + ), + IconButton( + icon: const Icon(Icons.layers_outlined), + onPressed: () => _showModelSelection(context), + color: AppColors.primaryAccent, + ), + ], + ), + ); + } + + Widget _buildToolCallingToggle(BuildContext context) { + return Card( + child: SwitchListTile( + title: const Text('Enable Tool Calling'), + subtitle: const Text('Allow LLM to use external tools'), + value: _toolCallingEnabled, + onChanged: (value) { + setState(() { + _toolCallingEnabled = value; + }); + unawaited(_saveSettings()); + }, + ), + ); + } + + Widget _buildRegisteredToolsSection(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Registered Tools (${_registeredTools.length})', + style: AppTypography.subheadline(context).copyWith( + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: AppSpacing.small), + ...(_registeredTools.map((tool) => _buildToolCard(context, tool))), + ], + ); + } + + Widget _buildToolCard(BuildContext context, ToolDefinition tool) { + return Card( + margin: const EdgeInsets.only(bottom: AppSpacing.small), + child: Padding( + padding: const EdgeInsets.all(AppSpacing.padding16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(Icons.build_outlined, size: 16, color: AppColors.primaryAccent), + const SizedBox(width: 8), + Text( + tool.name, + style: AppTypography.body(context).copyWith( + fontWeight: FontWeight.w600, + ), + ), + ], + ), + const SizedBox(height: 4), + Text( + tool.description, + style: AppTypography.caption(context).copyWith( + color: AppColors.textSecondary(context), + ), + ), + if (tool.parameters.isNotEmpty) ...[ + const SizedBox(height: 8), + Text( + 'Parameters: ${tool.parameters.map((p) => p.name).join(", ")}', + style: AppTypography.caption2(context).copyWith( + color: AppColors.textSecondary(context), + ), + ), + ], + ], + ), + ), + ); + } + + Widget _buildPromptInput(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Test Prompt', + style: AppTypography.subheadline(context).copyWith( + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: AppSpacing.small), + TextField( + controller: _promptController, + maxLines: 3, + decoration: InputDecoration( + hintText: 'Try: "What\'s the weather in San Francisco?"', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + filled: true, + fillColor: AppColors.backgroundSecondary(context), + ), + ), + ], + ); + } + + Widget _buildRunButton(BuildContext context) { + return SizedBox( + width: double.infinity, + child: ElevatedButton.icon( + onPressed: (_isGenerating || !_toolCallingEnabled) + ? null + : _runToolCalling, + icon: _isGenerating + ? const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator(strokeWidth: 2), + ) + : const Icon(Icons.play_arrow), + label: Text(_isGenerating ? 'Generating...' : 'Run with Tools'), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ); + } + + Widget _buildErrorMessage(BuildContext context) { + return Container( + padding: const EdgeInsets.all(AppSpacing.padding16), + decoration: BoxDecoration( + color: AppColors.primaryRed.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Icon(Icons.error_outline, color: AppColors.primaryRed), + const SizedBox(width: 8), + Expanded( + child: Text( + _errorMessage!, + style: TextStyle(color: AppColors.primaryRed), + ), + ), + ], + ), + ); + } + + Widget _buildExecutionLog(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Execution Log', + style: AppTypography.subheadline(context).copyWith( + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: AppSpacing.small), + Container( + width: double.infinity, + padding: const EdgeInsets.all(AppSpacing.padding16), + decoration: BoxDecoration( + color: AppColors.backgroundSecondary(context), + borderRadius: BorderRadius.circular(12), + ), + child: Text( + _toolExecutionLog, + style: AppTypography.caption(context).copyWith( + fontFamily: 'Menlo', + color: AppColors.textSecondary(context), + ), + ), + ), + ], + ); + } + + Widget _buildFinalResponse(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Final Response', + style: AppTypography.subheadline(context).copyWith( + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: AppSpacing.small), + Container( + width: double.infinity, + padding: const EdgeInsets.all(AppSpacing.padding16), + decoration: BoxDecoration( + color: AppColors.primaryAccent.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: AppColors.primaryAccent.withOpacity(0.3), + ), + ), + child: Text( + _finalResponse, + style: AppTypography.body(context), + ), + ), + ], + ); + } + + void _showModelSelection(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (sheetContext) => ModelSelectionSheet( + onModelSelected: (model) async { + // ModelSelectionSheet handles closing itself, so just sync state + unawaited(_syncModelState()); + }, + ), + ); + } +} diff --git a/examples/flutter/RunAnywhereAI/pubspec.yaml b/examples/flutter/RunAnywhereAI/pubspec.yaml index e4df5d7df..a678deb36 100644 --- a/examples/flutter/RunAnywhereAI/pubspec.yaml +++ b/examples/flutter/RunAnywhereAI/pubspec.yaml @@ -37,6 +37,8 @@ dependencies: package_info_plus: ^4.2.0 # URL launcher url_launcher: ^6.2.0 + # HTTP client for API calls + http: ^1.2.0 dev_dependencies: flutter_test: diff --git a/examples/ios/RunAnywhereAI/RunAnywhereAI/App/RunAnywhereAIApp.swift b/examples/ios/RunAnywhereAI/RunAnywhereAI/App/RunAnywhereAIApp.swift index 4fa568f2c..bb82395e7 100644 --- a/examples/ios/RunAnywhereAI/RunAnywhereAI/App/RunAnywhereAIApp.swift +++ b/examples/ios/RunAnywhereAI/RunAnywhereAI/App/RunAnywhereAIApp.swift @@ -204,7 +204,29 @@ struct RunAnywhereAIApp: App { memoryRequirement: 400_000_000 ) } - logger.info("โœ… LLM models registered") + + // Tool Calling Optimized Models + // LFM2-1.2B-Tool - Designed for concise and precise tool calling (Liquid AI) + if let lfm2ToolQ4URL = URL(string: "https://huggingface.co/LiquidAI/LFM2-1.2B-Tool-GGUF/resolve/main/LFM2-1.2B-Tool-Q4_K_M.gguf") { + RunAnywhere.registerModel( + id: "lfm2-1.2b-tool-q4_k_m", + name: "LiquidAI LFM2 1.2B Tool Q4_K_M", + url: lfm2ToolQ4URL, + framework: .llamaCpp, + memoryRequirement: 800_000_000 + ) + } + if let lfm2ToolQ8URL = URL(string: "https://huggingface.co/LiquidAI/LFM2-1.2B-Tool-GGUF/resolve/main/LFM2-1.2B-Tool-Q8_0.gguf") { + RunAnywhere.registerModel( + id: "lfm2-1.2b-tool-q8_0", + name: "LiquidAI LFM2 1.2B Tool Q8_0", + url: lfm2ToolQ8URL, + framework: .llamaCpp, + memoryRequirement: 1_400_000_000 + ) + } + + logger.info("โœ… LLM models registered (including tool-calling optimized models)") // Register ONNX STT and TTS models // Using tar.gz format hosted on RunanywhereAI/sherpa-onnx for fast native extraction diff --git a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Models/Message.swift b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Models/Message.swift index b4417b4e3..daec2c42b 100644 --- a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Models/Message.swift +++ b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Models/Message.swift @@ -18,6 +18,7 @@ public struct Message: Identifiable, Codable, Sendable { public let timestamp: Date public let analytics: MessageAnalytics? public let modelInfo: MessageModelInfo? + public let toolCallInfo: ToolCallInfo? public enum Role: String, Codable, Sendable { case system @@ -32,7 +33,8 @@ public struct Message: Identifiable, Codable, Sendable { thinkingContent: String? = nil, timestamp: Date = Date(), analytics: MessageAnalytics? = nil, - modelInfo: MessageModelInfo? = nil + modelInfo: MessageModelInfo? = nil, + toolCallInfo: ToolCallInfo? = nil ) { self.id = id self.role = role @@ -41,6 +43,39 @@ public struct Message: Identifiable, Codable, Sendable { self.timestamp = timestamp self.analytics = analytics self.modelInfo = modelInfo + self.toolCallInfo = toolCallInfo + } +} + +// MARK: - Tool Call Info + +public struct ToolCallInfo: Codable, Sendable { + public let toolName: String + public let arguments: String // JSON string for display + public let result: String? // JSON string for display + public let success: Bool + public let error: String? + + public init( + toolName: String, + arguments: [String: ToolValue], + result: [String: ToolValue]? = nil, + success: Bool, + error: String? = nil + ) { + self.toolName = toolName + self.success = success + self.error = error + + // Convert arguments to JSON string using ToolValue + self.arguments = ToolValue.object(arguments).toJSONString(pretty: true) ?? "{}" + + // Convert result to JSON string using ToolValue + if let result = result { + self.result = ToolValue.object(result).toJSONString(pretty: true) + } else { + self.result = nil + } } } diff --git a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/ViewModels/LLMViewModel+Analytics.swift b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/ViewModels/LLMViewModel+Analytics.swift index d65f983ff..874f297ad 100644 --- a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/ViewModels/LLMViewModel+Analytics.swift +++ b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/ViewModels/LLMViewModel+Analytics.swift @@ -46,8 +46,8 @@ extension LLMViewModel { ) -> MessageAnalytics { let completionStatus: MessageAnalytics.CompletionStatus = wasInterrupted ? .interrupted : .complete let generationParameters = MessageAnalytics.GenerationParameters( - temperature: Double(options.temperature ?? Float(LLMViewModel.defaultTemperatureValue)), - maxTokens: options.maxTokens ?? LLMViewModel.defaultMaxTokensValue, + temperature: Double(options.temperature), + maxTokens: options.maxTokens, topP: nil, topK: nil ) diff --git a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/ViewModels/LLMViewModel+ToolCalling.swift b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/ViewModels/LLMViewModel+ToolCalling.swift new file mode 100644 index 000000000..e376084fa --- /dev/null +++ b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/ViewModels/LLMViewModel+ToolCalling.swift @@ -0,0 +1,121 @@ +// +// LLMViewModel+ToolCalling.swift +// RunAnywhereAI +// +// Tool calling generation functionality for LLMViewModel +// + +import Foundation +import RunAnywhere + +extension LLMViewModel { + + // MARK: - Tool Calling Format Detection + + /// Determines the optimal tool calling format based on the model name/ID. + /// Different models are trained on different tool calling formats. + /// Returns format name string (C++ is single source of truth for valid formats). + private func detectToolCallFormat(for modelName: String?) -> String { + guard let name = modelName?.lowercased() else { + return ToolCallFormatName.default + } + + // LFM2-Tool models use Pythonic format: <|tool_call_start|>[func(args)]<|tool_call_end|> + if name.contains("lfm2") && name.contains("tool") { + return ToolCallFormatName.lfm2 + } + + // Default JSON format for general-purpose models + return ToolCallFormatName.default + } + + // MARK: - Tool Calling Generation + + func generateWithToolCalling( + prompt: String, + options: LLMGenerationOptions, + messageIndex: Int + ) async throws { + // Auto-detect the tool calling format based on the loaded model + let format = detectToolCallFormat(for: loadedModelName) + + // Get tool calling options with the appropriate format + let toolOptions = ToolCallingOptions( + maxToolCalls: 3, + autoExecute: true, + temperature: options.temperature, + maxTokens: options.maxTokens, + format: format + ) + + // Log the format being used for debugging + print("Using tool calling with format: \(format) for model: \(loadedModelName ?? "unknown")") + + // Generate with tools + let result = try await RunAnywhere.generateWithTools(prompt, options: toolOptions) + + // Extract tool call info if any tools were called + let toolCallInfo: ToolCallInfo? + if let lastCall = result.toolCalls.last, + let lastResult = result.toolResults.last { + toolCallInfo = ToolCallInfo( + toolName: lastCall.toolName, + arguments: lastCall.arguments, + result: lastResult.result, + success: lastResult.success, + error: lastResult.error + ) + } else { + toolCallInfo = nil + } + + // Update the message with the result + await updateMessageWithToolResult( + at: messageIndex, + text: result.text, + toolCallInfo: toolCallInfo + ) + } + + // MARK: - Message Updates + + func updateMessageWithToolResult( + at index: Int, + text: String, + toolCallInfo: ToolCallInfo? + ) async { + await MainActor.run { + guard index < self.messagesValue.count else { return } + + let currentMessage = self.messagesValue[index] + + let modelInfo: MessageModelInfo? + if let currentModel = ModelListViewModel.shared.currentModel { + modelInfo = MessageModelInfo(from: currentModel) + } else { + modelInfo = nil + } + + let updatedMessage = Message( + id: currentMessage.id, + role: currentMessage.role, + content: text, + thinkingContent: nil, + timestamp: currentMessage.timestamp, + analytics: nil, // Tool calling doesn't use standard analytics + modelInfo: modelInfo, + toolCallInfo: toolCallInfo + ) + + self.updateMessage(at: index, with: updatedMessage) + + // Save conversation + if let conversation = self.currentConversation { + var updatedConversation = conversation + updatedConversation.messages = self.messagesValue + updatedConversation.modelName = self.loadedModelName + self.conversationStore.updateConversation(updatedConversation) + } + } + } +} diff --git a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/ViewModels/LLMViewModel.swift b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/ViewModels/LLMViewModel.swift index 3c234ac06..301bd8cf0 100644 --- a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/ViewModels/LLMViewModel.swift +++ b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/ViewModels/LLMViewModel.swift @@ -37,6 +37,10 @@ final class LLMViewModel { var currentInput = "" var useStreaming = true + var useToolCalling: Bool { + get { ToolSettingsViewModel.shared.toolCallingEnabled } + set { ToolSettingsViewModel.shared.toolCallingEnabled = newValue } + } // MARK: - Dependencies @@ -241,6 +245,16 @@ final class LLMViewModel { options: LLMGenerationOptions, messageIndex: Int ) async throws { + // Check if tool calling is enabled and we have registered tools + let registeredTools = await RunAnywhere.getRegisteredTools() + let shouldUseToolCalling = useToolCalling && !registeredTools.isEmpty + + if shouldUseToolCalling { + logger.info("Using tool calling with \(registeredTools.count) registered tools") + try await generateWithToolCalling(prompt: prompt, options: options, messageIndex: messageIndex) + return + } + let modelSupportsStreaming = await RunAnywhere.supportsLLMStreaming let effectiveUseStreaming = useStreaming && modelSupportsStreaming diff --git a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ChatInterfaceView.swift b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ChatInterfaceView.swift index d3f35eb55..fba92646b 100644 --- a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ChatInterfaceView.swift +++ b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ChatInterfaceView.swift @@ -366,6 +366,12 @@ extension ChatInterfaceView { var inputArea: some View { VStack(spacing: 0) { Divider() + + // Tool calling indicator + if viewModel.useToolCalling { + toolCallingBadge + } + HStack(spacing: AppSpacing.mediumLarge) { TextField("Type a message...", text: $viewModel.currentInput, axis: .vertical) .textFieldStyle(.plain) @@ -397,6 +403,21 @@ extension ChatInterfaceView { .animation(.easeInOut(duration: AppLayout.animationFast), value: isTextFieldFocused) } } + + var toolCallingBadge: some View { + HStack(spacing: 6) { + Image(systemName: "wrench.and.screwdriver") + .font(.system(size: 10)) + Text("Tools enabled") + .font(AppTypography.caption2) + } + .foregroundColor(AppColors.primaryAccent) + .padding(.horizontal, 10) + .padding(.vertical, 4) + .background(AppColors.primaryAccent.opacity(0.1)) + .cornerRadius(6) + .padding(.top, 8) + } } // MARK: - Helper Methods diff --git a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ChatMessageComponents.swift b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ChatMessageComponents.swift index 6000f813f..3da5e05b6 100644 --- a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ChatMessageComponents.swift +++ b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ChatMessageComponents.swift @@ -67,11 +67,16 @@ struct MessageBubbleView: View { let message: Message let isGenerating: Bool @State private var isThinkingExpanded = false + @State private var showToolCallSheet = false var hasThinking: Bool { message.thinkingContent != nil && !(message.thinkingContent?.isEmpty ?? true) } + var hasToolCall: Bool { + message.toolCallInfo != nil + } + var body: some View { HStack { if message.role == .user { @@ -83,6 +88,10 @@ struct MessageBubbleView: View { thinkingSection } + if message.role == .assistant && hasToolCall { + toolCallSection + } + if message.role == .assistant && message.content.isEmpty && !(message.thinkingContent ?? "").isEmpty && @@ -99,6 +108,21 @@ struct MessageBubbleView: View { Spacer(minLength: AppSpacing.padding60) } } + .sheet(isPresented: $showToolCallSheet) { + if let toolCallInfo = message.toolCallInfo { + ToolCallDetailSheet(toolCallInfo: toolCallInfo) + .adaptiveSheetFrame() + } + } + } + + @ViewBuilder + var toolCallSection: some View { + if let toolCallInfo = message.toolCallInfo { + ToolCallIndicator(toolCallInfo: toolCallInfo) { + showToolCallSheet = true + } + } } } diff --git a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ToolCallViews.swift b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ToolCallViews.swift new file mode 100644 index 000000000..81fd2894d --- /dev/null +++ b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ToolCallViews.swift @@ -0,0 +1,210 @@ +// +// ToolCallViews.swift +// RunAnywhereAI +// +// Minimal UI components for tool calling visualization +// + +import SwiftUI +import RunAnywhere + +// MARK: - Tool Call Indicator + +struct ToolCallIndicator: View { + let toolCallInfo: ToolCallInfo + let onTap: () -> Void + + @State private var isPulsing = false + + var body: some View { + Button { + onTap() + } label: { + HStack(spacing: 6) { + Image(systemName: toolCallInfo.success ? "wrench.and.screwdriver" : "exclamationmark.triangle") + .font(.system(size: 12, weight: .medium)) + .foregroundColor(toolCallInfo.success ? AppColors.primaryAccent : AppColors.primaryOrange) + .scaleEffect(isPulsing ? 1.1 : 1.0) + + Text(toolCallInfo.toolName) + .font(AppTypography.caption2) + .foregroundColor(AppColors.textSecondary) + .lineLimit(1) + } + .padding(.horizontal, 10) + .padding(.vertical, 6) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(toolCallInfo.success + ? AppColors.primaryAccent.opacity(0.1) + : AppColors.primaryOrange.opacity(0.1)) + ) + .overlay( + RoundedRectangle(cornerRadius: 8) + .strokeBorder( + toolCallInfo.success + ? AppColors.primaryAccent.opacity(0.3) + : AppColors.primaryOrange.opacity(0.3), + lineWidth: 0.5 + ) + ) + } + .buttonStyle(PlainButtonStyle()) + } +} + +// MARK: - Tool Call Detail Sheet + +struct ToolCallDetailSheet: View { + let toolCallInfo: ToolCallInfo + @Environment(\.dismiss) private var dismiss + + var body: some View { + NavigationStack { + ScrollView { + VStack(alignment: .leading, spacing: 20) { + // Status + statusSection + + // Tool Name + detailSection(title: "Tool", content: toolCallInfo.toolName) + + // Arguments + codeSection(title: "Arguments", code: toolCallInfo.arguments) + + // Result + if let result = toolCallInfo.result { + codeSection(title: "Result", code: result) + } + + // Error + if let error = toolCallInfo.error { + detailSection(title: "Error", content: error, isError: true) + } + + Spacer() + } + .padding() + } + .background(AppColors.backgroundPrimary) + .navigationTitle("Tool Call") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + #endif + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button("Done") { dismiss() } + } + } + } + #if os(macOS) + .frame(minWidth: 400, minHeight: 350) + #endif + } + + private var statusSection: some View { + HStack(spacing: 10) { + Image(systemName: toolCallInfo.success ? "checkmark.circle.fill" : "xmark.circle.fill") + .font(.system(size: 24)) + .foregroundColor(toolCallInfo.success ? AppColors.statusGreen : AppColors.primaryRed) + + Text(toolCallInfo.success ? "Success" : "Failed") + .font(AppTypography.headline) + .foregroundColor(AppColors.textPrimary) + + Spacer() + } + .padding() + .background( + RoundedRectangle(cornerRadius: 12) + .fill(toolCallInfo.success + ? AppColors.statusGreen.opacity(0.1) + : AppColors.primaryRed.opacity(0.1)) + ) + } + + private func detailSection(title: String, content: String, isError: Bool = false) -> some View { + VStack(alignment: .leading, spacing: 8) { + Text(title) + .font(AppTypography.caption) + .foregroundColor(AppColors.textSecondary) + + Text(content) + .font(AppTypography.body) + .foregroundColor(isError ? AppColors.primaryRed : AppColors.textPrimary) + } + } + + private func codeSection(title: String, code: String) -> some View { + VStack(alignment: .leading, spacing: 8) { + Text(title) + .font(AppTypography.caption) + .foregroundColor(AppColors.textSecondary) + + Text(code) + .font(AppTypography.monospaced) + .foregroundColor(AppColors.textPrimary) + .padding(12) + .frame(maxWidth: .infinity, alignment: .leading) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(AppColors.backgroundSecondary) + ) + } + } +} + +// MARK: - Tool Calling Active Indicator + +struct ToolCallingActiveIndicator: View { + @State private var rotation: Double = 0 + + var body: some View { + HStack(spacing: 6) { + Image(systemName: "gearshape.2") + .font(.system(size: 12)) + .foregroundColor(AppColors.primaryAccent) + .rotationEffect(.degrees(rotation)) + .onAppear { + withAnimation(.linear(duration: 2).repeatForever(autoreverses: false)) { + rotation = 360 + } + } + + Text("Calling tool...") + .font(AppTypography.caption2) + .foregroundColor(AppColors.textSecondary) + } + .padding(.horizontal, 10) + .padding(.vertical, 6) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(AppColors.primaryAccent.opacity(0.1)) + ) + } +} + +#Preview { + VStack(spacing: 20) { + ToolCallIndicator( + toolCallInfo: ToolCallInfo( + toolName: "get_weather", + arguments: ["location": .string("San Francisco")], + result: ["temp": .number(72), "condition": .string("Sunny")], + success: true + ) + ) {} + + ToolCallIndicator( + toolCallInfo: ToolCallInfo( + toolName: "search_web", + arguments: ["query": .string("Swift concurrency")], + success: false, + error: "Network timeout" + ) + ) {} + + ToolCallingActiveIndicator() + } + .padding() +} diff --git a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Models/ModelSelectionSheet.swift b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Models/ModelSelectionSheet.swift index 25b609c12..e1939875a 100644 --- a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Models/ModelSelectionSheet.swift +++ b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Models/ModelSelectionSheet.swift @@ -258,14 +258,15 @@ extension ModelSelectionSheet { (context == .voice && [.language, .multimodal].contains(model.category)) if isLLM { - await viewModel.setCurrentModel(model) await MainActor.run { + viewModel.setCurrentModel(model) NotificationCenter.default.post( name: Notification.Name("ModelLoaded"), object: model ) } } + await onModelSelected(model) } } diff --git a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Settings/CombinedSettingsView.swift b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Settings/CombinedSettingsView.swift index 26c0f17f9..dc74443c6 100644 --- a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Settings/CombinedSettingsView.swift +++ b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Settings/CombinedSettingsView.swift @@ -13,13 +13,14 @@ import Combine struct CombinedSettingsView: View { // ViewModel - all business logic is here @StateObject private var viewModel = SettingsViewModel() + @StateObject private var toolViewModel = ToolSettingsViewModel.shared var body: some View { Group { #if os(macOS) - MacOSSettingsContent(viewModel: viewModel) + MacOSSettingsContent(viewModel: viewModel, toolViewModel: toolViewModel) #else - IOSSettingsContent(viewModel: viewModel) + IOSSettingsContent(viewModel: viewModel, toolViewModel: toolViewModel) #endif } .sheet(isPresented: $viewModel.showApiKeyEntry) { @@ -27,6 +28,7 @@ struct CombinedSettingsView: View { } .task { await viewModel.loadStorageData() + await toolViewModel.refreshRegisteredTools() } .alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) { Button("OK") { @@ -51,6 +53,7 @@ struct CombinedSettingsView: View { private struct IOSSettingsContent: View { @ObservedObject var viewModel: SettingsViewModel + @ObservedObject var toolViewModel: ToolSettingsViewModel var body: some View { Form { @@ -71,6 +74,9 @@ private struct IOSSettingsContent: View { ) } + // Tool Calling Settings + ToolSettingsSection(viewModel: toolViewModel) + // API Configuration (for testing custom backend) Section { Button( @@ -230,6 +236,7 @@ private struct IOSSettingsContent: View { private struct MacOSSettingsContent: View { @ObservedObject var viewModel: SettingsViewModel + @ObservedObject var toolViewModel: ToolSettingsViewModel var body: some View { ScrollView { @@ -239,6 +246,7 @@ private struct MacOSSettingsContent: View { .padding(.bottom, AppSpacing.medium) GenerationSettingsCard(viewModel: viewModel) + ToolSettingsCard(viewModel: toolViewModel) APIConfigurationCard(viewModel: viewModel) StorageCard(viewModel: viewModel) DownloadedModelsCard(viewModel: viewModel) @@ -728,6 +736,12 @@ private struct StoredModelRow: View { @State private var showingDeleteConfirmation = false @State private var isDeleting = false + private var isDeletable: Bool { + // Platform models (built-in) can't be deleted + guard let framework = model.framework else { return false } + return framework != .foundationModels && framework != .systemTTS + } + var body: some View { VStack(alignment: .leading, spacing: AppSpacing.smallMedium) { HStack { @@ -757,20 +771,23 @@ private struct StoredModelRow: View { .tint(AppColors.primaryAccent) .controlSize(.mini) - Button( - action: { - showingDeleteConfirmation = true - }, - label: { - Image(systemName: "trash") - .foregroundColor(AppColors.primaryRed) - } - ) - .font(AppTypography.caption2) - .buttonStyle(.bordered) - .tint(AppColors.primaryRed) - .controlSize(.mini) - .disabled(isDeleting) + // ONLY show delete button if deletable + if isDeletable { + Button( + action: { + showingDeleteConfirmation = true + }, + label: { + Image(systemName: "trash") + .foregroundColor(AppColors.primaryRed) + } + ) + .font(AppTypography.caption2) + .buttonStyle(.bordered) + .tint(AppColors.primaryRed) + .controlSize(.mini) + .disabled(isDeleting) + } } } } diff --git a/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Settings/ToolSettingsView.swift b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Settings/ToolSettingsView.swift new file mode 100644 index 000000000..dec41a8ef --- /dev/null +++ b/examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Settings/ToolSettingsView.swift @@ -0,0 +1,403 @@ +// +// ToolSettingsView.swift +// RunAnywhereAI +// +// Tool registration and management settings +// + +import SwiftUI +import RunAnywhere + +// MARK: - Tool Settings View Model + +@MainActor +class ToolSettingsViewModel: ObservableObject { + static let shared = ToolSettingsViewModel() + + @Published var registeredTools: [ToolDefinition] = [] + @Published var toolCallingEnabled: Bool = false { + didSet { + UserDefaults.standard.set(toolCallingEnabled, forKey: "toolCallingEnabled") + } + } + + // Built-in demo tools with REAL API implementations + private var demoTools: [(definition: ToolDefinition, executor: ToolExecutor)] { + [ + // Weather Tool - Uses Open-Meteo API (free, no API key required) + ( + definition: ToolDefinition( + name: "get_weather", + description: "Gets the current weather for a given location using Open-Meteo API", + parameters: [ + ToolParameter( + name: "location", + type: .string, + description: "City name (e.g., 'San Francisco', 'London', 'Tokyo')" + ) + ], + category: "Utility" + ), + executor: { args in + try await WeatherService.fetchWeather(for: args["location"]?.stringValue ?? "San Francisco") + } + ), + // Time Tool - Real system time with timezone + ( + definition: ToolDefinition( + name: "get_current_time", + description: "Gets the current date, time, and timezone information", + parameters: [], + category: "Utility" + ), + executor: { _ in + let now = Date() + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .full + dateFormatter.timeStyle = .medium + + let timeZone = TimeZone.current + let timeFormatter = DateFormatter() + timeFormatter.dateFormat = "HH:mm:ss" + + return [ + "datetime": .string(dateFormatter.string(from: now)), + "time": .string(timeFormatter.string(from: now)), + "timestamp": .string(ISO8601DateFormatter().string(from: now)), + "timezone": .string(timeZone.identifier), + "utc_offset": .string(timeZone.abbreviation() ?? "UTC") + ] + } + ), + // Calculator Tool - Real math evaluation + ( + definition: ToolDefinition( + name: "calculate", + description: "Performs math calculations. Supports +, -, *, /, and parentheses", + parameters: [ + ToolParameter( + name: "expression", + type: .string, + description: "Math expression (e.g., '2 + 2 * 3', '(10 + 5) / 3')" + ) + ], + category: "Utility" + ), + executor: { args in + let expression = args["expression"]?.stringValue ?? args["input"]?.stringValue ?? "0" + // Clean the expression - remove any non-math characters + let cleanedExpression = expression + .replacingOccurrences(of: "=", with: "") + .replacingOccurrences(of: "x", with: "*") + .replacingOccurrences(of: "ร—", with: "*") + .replacingOccurrences(of: "รท", with: "/") + .trimmingCharacters(in: .whitespacesAndNewlines) + + do { + let exp = NSExpression(format: cleanedExpression) + if let result = exp.expressionValue(with: nil, context: nil) as? NSNumber { + return [ + "result": .number(result.doubleValue), + "expression": .string(expression) + ] + } + } catch { + // Fall through to error + } + return [ + "error": .string("Could not evaluate expression: \(expression)"), + "expression": .string(expression) + ] + } + ) + ] + } + + init() { + toolCallingEnabled = UserDefaults.standard.bool(forKey: "toolCallingEnabled") + Task { + await refreshRegisteredTools() + } + } + + func refreshRegisteredTools() async { + registeredTools = await RunAnywhere.getRegisteredTools() + } + + func registerDemoTools() async { + for tool in demoTools { + await RunAnywhere.registerTool(tool.definition, executor: tool.executor) + } + await refreshRegisteredTools() + } + + func clearAllTools() async { + await RunAnywhere.clearTools() + await refreshRegisteredTools() + } +} + +// MARK: - Tool Settings Section (iOS) + +struct ToolSettingsSection: View { + @ObservedObject var viewModel: ToolSettingsViewModel + + var body: some View { + Section { + Toggle("Enable Tool Calling", isOn: $viewModel.toolCallingEnabled) + + if viewModel.toolCallingEnabled { + HStack { + Text("Registered Tools") + Spacer() + Text("\(viewModel.registeredTools.count)") + .foregroundColor(AppColors.textSecondary) + } + + if viewModel.registeredTools.isEmpty { + Button("Add Demo Tools") { + Task { + await viewModel.registerDemoTools() + } + } + .foregroundColor(AppColors.primaryAccent) + } else { + ForEach(viewModel.registeredTools, id: \.name) { tool in + ToolRow(tool: tool) + } + + Button("Clear All Tools") { + Task { + await viewModel.clearAllTools() + } + } + .foregroundColor(AppColors.primaryRed) + } + } + } header: { + Text("Tool Calling") + } footer: { + Text("Allow the LLM to use registered tools to perform actions like getting weather, time, or calculations.") + .font(AppTypography.caption) + } + } +} + +// MARK: - Tool Settings Card (macOS) + +struct ToolSettingsCard: View { + @ObservedObject var viewModel: ToolSettingsViewModel + + var body: some View { + VStack(alignment: .leading, spacing: AppSpacing.xLarge) { + Text("Tool Calling") + .font(AppTypography.headline) + .foregroundColor(AppColors.textSecondary) + + VStack(alignment: .leading, spacing: AppSpacing.large) { + HStack { + Text("Enable Tool Calling") + .frame(width: 150, alignment: .leading) + Toggle("", isOn: $viewModel.toolCallingEnabled) + Spacer() + Text(viewModel.toolCallingEnabled ? "Enabled" : "Disabled") + .font(AppTypography.caption) + .foregroundColor(viewModel.toolCallingEnabled ? AppColors.statusGreen : AppColors.textSecondary) + } + + if viewModel.toolCallingEnabled { + Divider() + + HStack { + Text("Registered Tools") + Spacer() + Text("\(viewModel.registeredTools.count)") + .font(AppTypography.monospaced) + .foregroundColor(AppColors.primaryAccent) + } + + if viewModel.registeredTools.isEmpty { + Button("Add Demo Tools") { + Task { + await viewModel.registerDemoTools() + } + } + .buttonStyle(.bordered) + .tint(AppColors.primaryAccent) + } else { + ForEach(viewModel.registeredTools, id: \.name) { tool in + ToolRow(tool: tool) + } + + Button("Clear All Tools") { + Task { + await viewModel.clearAllTools() + } + } + .buttonStyle(.bordered) + .tint(AppColors.primaryRed) + } + } + + Text("Allow the LLM to use registered tools to perform actions like getting weather, time, or calculations.") + .font(AppTypography.caption) + .foregroundColor(AppColors.textSecondary) + } + .padding(AppSpacing.large) + .background(AppColors.backgroundSecondary) + .cornerRadius(AppSpacing.cornerRadiusLarge) + } + } +} + +// MARK: - Tool Row + +struct ToolRow: View { + let tool: ToolDefinition + + var body: some View { + VStack(alignment: .leading, spacing: 4) { + HStack { + Image(systemName: "wrench.and.screwdriver") + .font(.system(size: 12)) + .foregroundColor(AppColors.primaryAccent) + Text(tool.name) + .font(AppTypography.subheadlineMedium) + } + Text(tool.description) + .font(AppTypography.caption) + .foregroundColor(AppColors.textSecondary) + .lineLimit(2) + + if !tool.parameters.isEmpty { + HStack(spacing: 4) { + Text("Params:") + .font(AppTypography.caption2) + .foregroundColor(AppColors.textSecondary) + ForEach(tool.parameters, id: \.name) { param in + Text(param.name) + .font(AppTypography.caption2) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background(AppColors.backgroundTertiary) + .cornerRadius(4) + } + } + } + } + .padding(.vertical, 4) + } +} + +// MARK: - Weather Service (Open-Meteo API) + +/// Real weather service using Open-Meteo API (free, no API key required) +enum WeatherService { + // Open-Meteo Geocoding API + private static let geocodingURL = "https://geocoding-api.open-meteo.com/v1/search" + // Open-Meteo Weather API + private static let weatherURL = "https://api.open-meteo.com/v1/forecast" + + /// Fetch real weather data for a location + static func fetchWeather(for location: String) async throws -> [String: ToolValue] { + // Step 1: Geocode the location to get coordinates + guard let coordinates = try await geocodeLocation(location) else { + return [ + "error": .string("Could not find location: \(location)"), + "location": .string(location) + ] + } + + // Step 2: Fetch weather for coordinates + return try await fetchWeatherForCoordinates( + latitude: coordinates.latitude, + longitude: coordinates.longitude, + locationName: coordinates.name + ) + } + + private static func geocodeLocation(_ location: String) async throws -> (latitude: Double, longitude: Double, name: String)? { + guard let encodedLocation = location.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), + let url = URL(string: "\(geocodingURL)?name=\(encodedLocation)&count=1&language=en&format=json") else { + return nil + } + + let (data, _) = try await URLSession.shared.data(from: url) + + guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let results = json["results"] as? [[String: Any]], + let first = results.first, + let latitude = first["latitude"] as? Double, + let longitude = first["longitude"] as? Double else { + return nil + } + + let name = first["name"] as? String ?? location + return (latitude, longitude, name) + } + + private static func fetchWeatherForCoordinates( + latitude: Double, + longitude: Double, + locationName: String + ) async throws -> [String: ToolValue] { + let urlString = "\(weatherURL)?latitude=\(latitude)&longitude=\(longitude)" + + "¤t=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m" + + "&temperature_unit=fahrenheit&wind_speed_unit=mph" + + guard let url = URL(string: urlString) else { + return ["error": .string("Invalid weather API URL")] + } + + let (data, _) = try await URLSession.shared.data(from: url) + + guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let current = json["current"] as? [String: Any] else { + return ["error": .string("Could not parse weather data")] + } + + let temperature = current["temperature_2m"] as? Double ?? 0 + let humidity = current["relative_humidity_2m"] as? Double ?? 0 + let windSpeed = current["wind_speed_10m"] as? Double ?? 0 + let weatherCode = current["weather_code"] as? Int ?? 0 + + return [ + "location": .string(locationName), + "temperature": .number(temperature), + "unit": .string("fahrenheit"), + "humidity": .number(humidity), + "wind_speed_mph": .number(windSpeed), + "condition": .string(weatherCodeToCondition(weatherCode)) + ] + } + + /// Convert WMO weather code to human-readable condition + private static func weatherCodeToCondition(_ code: Int) -> String { + switch code { + case 0: return "Clear sky" + case 1: return "Mainly clear" + case 2: return "Partly cloudy" + case 3: return "Overcast" + case 45, 48: return "Foggy" + case 51, 53, 55: return "Drizzle" + case 56, 57: return "Freezing drizzle" + case 61, 63, 65: return "Rain" + case 66, 67: return "Freezing rain" + case 71, 73, 75: return "Snow" + case 77: return "Snow grains" + case 80, 81, 82: return "Rain showers" + case 85, 86: return "Snow showers" + case 95: return "Thunderstorm" + case 96, 99: return "Thunderstorm with hail" + default: return "Unknown" + } + } +} + +#Preview { + NavigationStack { + Form { + ToolSettingsSection(viewModel: ToolSettingsViewModel.shared) + } + } +} diff --git a/examples/react-native/RunAnywhereAI/.gitignore b/examples/react-native/RunAnywhereAI/.gitignore index f481834b8..cc50db3dc 100644 --- a/examples/react-native/RunAnywhereAI/.gitignore +++ b/examples/react-native/RunAnywhereAI/.gitignore @@ -1,6 +1,11 @@ # Dependencies node_modules/ +# Yarn +.yarn/ +.pnp.cjs +.pnp.loader.mjs + # React Native .expo/ *.jks @@ -33,6 +38,7 @@ ios/.xcode.env.local # Android android/.gradle/ android/app/build/ +android/app/.cxx/ android/build/ android/local.properties android/*.iml diff --git a/examples/react-native/RunAnywhereAI/.yarn/install-state.gz b/examples/react-native/RunAnywhereAI/.yarn/install-state.gz deleted file mode 100644 index e204cd28d..000000000 Binary files a/examples/react-native/RunAnywhereAI/.yarn/install-state.gz and /dev/null differ diff --git a/examples/react-native/RunAnywhereAI/App.tsx b/examples/react-native/RunAnywhereAI/App.tsx index 9da41b7df..7367ac559 100644 --- a/examples/react-native/RunAnywhereAI/App.tsx +++ b/examples/react-native/RunAnywhereAI/App.tsx @@ -5,8 +5,9 @@ * * Architecture Pattern: * - Two-phase SDK initialization (matching iOS pattern) - * - Module registration with models (LlamaCPP, ONNX, FluidAudio) - * - Tab-based navigation with 5 tabs (Chat, STT, TTS, Voice, Settings) + * - Module registration with models (LlamaCPP, ONNX) + * - Tab-based navigation with 5 tabs (Chat, Transcribe, Speak, Voice, Settings) + * - Tool calling settings are in Settings tab (matching iOS) * * Reference: iOS examples/ios/RunAnywhereAI/RunAnywhereAI/App/RunAnywhereAIApp.swift */ @@ -132,6 +133,13 @@ const App: React.FC = () => { url: 'https://huggingface.co/Triangle104/Qwen2.5-0.5B-Instruct-Q6_K-GGUF/resolve/main/qwen2.5-0.5b-instruct-q6_k.gguf', memoryRequirement: 600_000_000, }); + // Llama 3.2 3B - Ideal for tool calling on mobile (3B params, ~1.8GB) + await LlamaCPP.addModel({ + id: 'llama-3.2-3b-instruct-q4_k_m', + name: 'Llama 3.2 3B Instruct Q4_K_M (Tool Calling)', + url: 'https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF/resolve/main/Llama-3.2-3B-Instruct-Q4_K_M.gguf', + memoryRequirement: 2_000_000_000, + }); await LlamaCPP.addModel({ id: 'lfm2-350m-q4_k_m', name: 'LiquidAI LFM2 350M Q4_K_M', @@ -144,6 +152,28 @@ const App: React.FC = () => { url: 'https://huggingface.co/LiquidAI/LFM2-350M-GGUF/resolve/main/LFM2-350M-Q8_0.gguf', memoryRequirement: 400_000_000, }); + // LFM2.5 1.2B - Best-in-class edge model from Liquid AI (1.2B params, ~700MB Q4) + // 239 tok/s on AMD CPU, designed for on-device deployment + await LlamaCPP.addModel({ + id: 'lfm2.5-1.2b-instruct-q4_k_m', + name: 'LiquidAI LFM2.5 1.2B Instruct Q4_K_M', + url: 'https://huggingface.co/LiquidAI/LFM2.5-1.2B-Instruct-GGUF/resolve/main/LFM2.5-1.2B-Instruct-Q4_K_M.gguf', + memoryRequirement: 900_000_000, + }); + // Tool Calling Optimized Models + // LFM2-1.2B-Tool - Designed for concise and precise tool calling (Liquid AI) + await LlamaCPP.addModel({ + id: 'lfm2-1.2b-tool-q4_k_m', + name: 'LiquidAI LFM2 1.2B Tool Q4_K_M', + url: 'https://huggingface.co/LiquidAI/LFM2-1.2B-Tool-GGUF/resolve/main/LFM2-1.2B-Tool-Q4_K_M.gguf', + memoryRequirement: 800_000_000, + }); + await LlamaCPP.addModel({ + id: 'lfm2-1.2b-tool-q8_0', + name: 'LiquidAI LFM2 1.2B Tool Q8_0', + url: 'https://huggingface.co/LiquidAI/LFM2-1.2B-Tool-GGUF/resolve/main/LFM2-1.2B-Tool-Q8_0.gguf', + memoryRequirement: 1_400_000_000, + }); // ONNX module with STT and TTS models // Using tar.gz format hosted on RunanywhereAI/sherpa-onnx for fast native extraction diff --git a/examples/react-native/RunAnywhereAI/ios/RunAnywhereAI.xcodeproj/project.pbxproj b/examples/react-native/RunAnywhereAI/ios/RunAnywhereAI.xcodeproj/project.pbxproj index c7e0ac532..b05cb9c71 100644 --- a/examples/react-native/RunAnywhereAI/ios/RunAnywhereAI.xcodeproj/project.pbxproj +++ b/examples/react-native/RunAnywhereAI/ios/RunAnywhereAI.xcodeproj/project.pbxproj @@ -476,7 +476,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = AFAL2647U9; + DEVELOPMENT_TEAM = L86FH3K93L; ENABLE_BITCODE = NO; INFOPLIST_FILE = RunAnywhereAI/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -505,7 +505,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = AFAL2647U9; + DEVELOPMENT_TEAM = L86FH3K93L; INFOPLIST_FILE = RunAnywhereAI/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/examples/react-native/RunAnywhereAI/ios/RunAnywhereAI/PrivacyInfo.xcprivacy b/examples/react-native/RunAnywhereAI/ios/RunAnywhereAI/PrivacyInfo.xcprivacy index bad327615..41b8317f0 100644 --- a/examples/react-native/RunAnywhereAI/ios/RunAnywhereAI/PrivacyInfo.xcprivacy +++ b/examples/react-native/RunAnywhereAI/ios/RunAnywhereAI/PrivacyInfo.xcprivacy @@ -6,18 +6,18 @@ NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPICategoryFileTimestamp NSPrivacyAccessedAPITypeReasons - CA92.1 + C617.1 NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPICategoryUserDefaults NSPrivacyAccessedAPITypeReasons - C617.1 + CA92.1 diff --git a/examples/react-native/RunAnywhereAI/package-lock.json b/examples/react-native/RunAnywhereAI/package-lock.json index f66fde1ca..39e136a47 100644 --- a/examples/react-native/RunAnywhereAI/package-lock.json +++ b/examples/react-native/RunAnywhereAI/package-lock.json @@ -285,7 +285,7 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", @@ -375,7 +375,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", @@ -450,7 +450,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.28.6", @@ -493,7 +493,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.27.1.tgz", "integrity": "sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -560,7 +560,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -573,7 +573,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.28.6.tgz", "integrity": "sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -589,7 +589,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.28.6.tgz", "integrity": "sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -791,7 +791,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.6.tgz", "integrity": "sha512-9knsChgsMzBV5Yh3kkhrZNxH3oCYAfMBkNNaVN4cP2RVlFPe8wYdwwcnOsAbkdDoV9UjFtOXWrWB52M8W4jNeA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", @@ -809,7 +809,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.28.6", @@ -827,7 +827,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -843,7 +843,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", @@ -860,7 +860,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -881,7 +881,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", @@ -898,7 +898,7 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -915,7 +915,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz", "integrity": "sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -932,7 +932,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -949,7 +949,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", @@ -967,7 +967,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -983,7 +983,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -1015,7 +1015,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -1032,7 +1032,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -1048,7 +1048,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -1064,7 +1064,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.28.6", @@ -1084,7 +1084,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -1100,7 +1100,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", @@ -1117,7 +1117,7 @@ "version": "7.27.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1133,7 +1133,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", @@ -1150,7 +1150,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -1168,7 +1168,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1184,7 +1184,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -1204,7 +1204,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1220,7 +1220,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1236,7 +1236,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.6.tgz", "integrity": "sha512-eZhoEZHYQLL5uc1gS5e9/oTknS0sSSAtd5TkKMUp3J+S/CaUjagc0kOUPsEbDmMeva0nC3WWl4SxVY6+OBuxfw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -1252,7 +1252,7 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.5.tgz", "integrity": "sha512-20NUVgOrinudkIBzQ2bNxP08YpKprUkRTiRSd2/Z5GOdPImJGkoN4Z7IQe1T5AdyKI1i5L6RBmluqdSzvaq9/w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", @@ -1288,7 +1288,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", @@ -1305,7 +1305,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1609,14 +1609,14 @@ "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@hapi/topo": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" @@ -1995,7 +1995,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -2009,7 +2009,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -2019,7 +2019,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -2341,7 +2341,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-20.1.0.tgz", "integrity": "sha512-441WsVtRe4nGJ9OzA+QMU1+22lA6Q2hRWqqIMKD0wjEMLqcSfOZyu2UL9a/yRpL/dRpyUsU4n7AxqKfTKO/Csg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-clean": "20.1.0", @@ -2371,7 +2371,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-20.1.0.tgz", "integrity": "sha512-77L4DifWfxAT8ByHnkypge7GBMYpbJAjBGV+toowt5FQSGaTBDcBHCX+FFqFRukD5fH6i8sZ41Gtw+nbfCTTIA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "20.1.0", @@ -2384,7 +2384,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-20.1.0.tgz", "integrity": "sha512-1x9rhLLR/dKKb92Lb5O0l0EmUG08FHf+ZVyVEf9M+tX+p5QIm52MRiy43R0UAZ2jJnFApxRk+N3sxoYK4Dtnag==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "20.1.0", @@ -2399,7 +2399,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-config-android/-/cli-config-android-20.1.0.tgz", "integrity": "sha512-3A01ZDyFeCALzzPcwP/fleHoP3sGNq1UX7FzxkTrOFX8RRL9ntXNXQd27E56VU4BBxGAjAJT4Utw8pcOjJceIA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "20.1.0", @@ -2412,7 +2412,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-config-apple/-/cli-config-apple-20.1.0.tgz", "integrity": "sha512-n6JVs8Q3yxRbtZQOy05ofeb1kGtspGN3SgwPmuaqvURF9fsuS7c4/9up2Kp9C+1D2J1remPJXiZLNGOcJvfpOA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "20.1.0", @@ -2425,7 +2425,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-20.1.0.tgz", "integrity": "sha512-QfJF1GVjA4PBrIT3SJ0vFFIu0km1vwOmLDlOYVqfojajZJ+Dnvl0f94GN1il/jT7fITAxom///XH3/URvi7YTQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-config": "20.1.0", @@ -2449,7 +2449,7 @@ "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "devOptional": true, + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -2462,7 +2462,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-20.1.0.tgz", "integrity": "sha512-TeHPDThOwDppQRpndm9kCdRCBI8AMy3HSIQ+iy7VYQXL5BtZ5LfmGdusoj7nVN/ZGn0Lc6Gwts5qowyupXdeKg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-config-android": "20.1.0", @@ -2476,7 +2476,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-20.1.0.tgz", "integrity": "sha512-0ih1hrYezSM2cuOlVnwBEFtMwtd8YgpTLmZauDJCv50rIumtkI1cQoOgLoS4tbPCj9U/Vn2a9BFH0DLFOOIacg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-config-apple": "20.1.0", @@ -2490,7 +2490,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-20.1.0.tgz", "integrity": "sha512-XN7Da9z4WsJxtqVtEzY8q2bv22OsvzaFP5zy5+phMWNoJlU4lf7IvBSxqGYMpQ9XhYP7arDw5vmW4W34s06rnA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-platform-apple": "20.1.0" @@ -2500,7 +2500,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-20.1.0.tgz", "integrity": "sha512-Tb415Oh8syXNT2zOzLzFkBXznzGaqKCiaichxKzGCDKg6JGHp3jSuCmcTcaPeYC7oc32n/S3Psw7798r4Q/7lA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "20.1.0", @@ -2519,7 +2519,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-20.1.0.tgz", "integrity": "sha512-/YmzHGOkY6Bgrv4OaA1L8rFqsBlQd1EB2/ipAoKPiieV0EcB5PUamUSuNeFU3sBZZTYQCUENwX4wgOHgFUlDnQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@vscode/sudo-prompt": "^9.0.0", @@ -2538,7 +2538,7 @@ "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "devOptional": true, + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -2551,7 +2551,7 @@ "version": "20.1.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-20.1.0.tgz", "integrity": "sha512-D0kDspcwgbVXyNjwicT7Bb1JgXjijTw1JJd+qxyF/a9+sHv7TU4IchV+gN38QegeXqVyM4Ym7YZIvXMFBmyJqA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "joi": "^17.2.1" @@ -2561,7 +2561,7 @@ "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "devOptional": true, + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -2583,7 +2583,7 @@ "version": "0.83.1", "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.83.1.tgz", "integrity": "sha512-VPj8O3pG1ESjZho9WVKxqiuryrotAECPHGF5mx46zLUYNTWR5u9OMUXYk7LeLy+JLWdGEZ2Gn3KoXeFZbuqE+g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.3", @@ -2597,7 +2597,7 @@ "version": "0.83.1", "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.83.1.tgz", "integrity": "sha512-xI+tbsD4fXcI6PVU4sauRCh0a5fuLQC849SINmU2J5wP8kzKu4Ye0YkGjUW3mfGrjaZcjkWmF6s33jpyd3gdTw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", @@ -3139,7 +3139,7 @@ "version": "0.83.1", "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.83.1.tgz", "integrity": "sha512-fqt6DHWX1GBGDKa5WJOjDtPPy2M9lkYVLn59fBeFQ0GXhBRzNbUh8JzWWI/Q2CLDZ2tgKCcwaiXJ1OHWVd2BCQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", @@ -3158,7 +3158,7 @@ "version": "0.83.1", "resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.83.1.tgz", "integrity": "sha512-1rjYZf62fCm6QAinHmRAKnJxIypX0VF/zBPd0qWvWABMZugrS0eACuIbk9Wk0StBod4yL8KnwEJyg77ak8xYzQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native/js-polyfills": "0.83.1", @@ -3327,7 +3327,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" @@ -3337,14 +3337,14 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@sideway/pinpoint": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@sinclair/typebox": { @@ -3475,7 +3475,7 @@ "version": "19.1.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz", "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -3806,7 +3806,7 @@ "version": "9.3.2", "resolved": "https://registry.npmjs.org/@vscode/sudo-prompt/-/sudo-prompt-9.3.2.tgz", "integrity": "sha512-gcXoCN00METUNFeQOFJ+C9xUI0DKB+0EGMVg7wbVYRHBw2Eq3fKisDZOkRdOz3kqXRKOENMfShPOmypw1/8nOw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/abort-controller": { @@ -3901,7 +3901,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/ansi-fragments/-/ansi-fragments-0.2.1.tgz", "integrity": "sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "colorette": "^1.0.7", @@ -3913,7 +3913,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3923,7 +3923,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" @@ -3985,14 +3985,14 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.7.tgz", "integrity": "sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "devOptional": true, + "dev": true, "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { @@ -4153,7 +4153,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -4173,7 +4173,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/available-typed-arrays": { @@ -4248,7 +4248,7 @@ "version": "0.4.14", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.27.7", @@ -4263,7 +4263,7 @@ "version": "0.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", @@ -4277,7 +4277,7 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" @@ -4299,7 +4299,7 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/plugin-syntax-flow": "^7.12.1" @@ -4391,7 +4391,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "buffer": "^5.5.0", @@ -4403,7 +4403,7 @@ "version": "1.20.4", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -4428,7 +4428,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -4438,7 +4438,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/brace-expansion": { @@ -4509,7 +4509,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -4540,7 +4540,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -4569,7 +4569,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4583,7 +4583,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -4600,7 +4600,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4720,7 +4720,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" @@ -4733,7 +4733,7 @@ "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4760,7 +4760,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.8" @@ -4811,21 +4811,21 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/commander": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": "^12.20.0 || >=14" @@ -4835,7 +4835,7 @@ "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "mime-db": ">= 1.43.0 < 2" @@ -4848,7 +4848,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -4867,7 +4867,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -4877,7 +4877,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/concat-map": { @@ -4920,7 +4920,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -4936,7 +4936,7 @@ "version": "3.47.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.47.0.tgz", "integrity": "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "browserslist": "^4.28.0" @@ -4950,7 +4950,7 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.1", @@ -4991,7 +4991,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/data-view-buffer": { @@ -5052,7 +5052,7 @@ "version": "1.11.19", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/debug": { @@ -5076,7 +5076,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5102,7 +5102,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5112,7 +5112,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "clone": "^1.0.2" @@ -5206,7 +5206,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -5248,7 +5248,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5258,7 +5258,7 @@ "version": "7.21.0", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "envinfo": "dist/cli.js" @@ -5271,7 +5271,7 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -5290,7 +5290,7 @@ "version": "1.5.2", "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.2.tgz", "integrity": "sha512-kNAL7hESndBCrWwS72QyV3IVOTrVmj9D062FV5BQswNL5zEdeRmz/WJFyh6Aj/plvvSOrzddkxW57HgkZcR9Fw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -5377,7 +5377,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5387,7 +5387,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5425,7 +5425,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -6018,7 +6018,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", @@ -6061,7 +6061,7 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -6078,7 +6078,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -6104,7 +6104,7 @@ "version": "4.5.3", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -6123,7 +6123,7 @@ "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -6261,7 +6261,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -6347,7 +6347,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -6382,7 +6382,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "devOptional": true, + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6451,7 +6451,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -6485,7 +6485,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -6499,7 +6499,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -6640,7 +6640,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6717,7 +6717,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6746,7 +6746,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -6837,7 +6837,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=10.17.0" @@ -6857,7 +6857,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -6870,7 +6870,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -6916,7 +6916,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -7001,7 +7001,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/is-async-function": { @@ -7074,7 +7074,7 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -7140,7 +7140,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7166,7 +7166,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -7196,7 +7196,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -7209,7 +7209,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7338,7 +7338,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7402,7 +7402,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -7461,7 +7461,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -7722,7 +7722,7 @@ "version": "17.13.3", "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.3.0", @@ -7742,7 +7742,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -7780,7 +7780,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { @@ -7813,7 +7813,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "devOptional": true, + "dev": true, "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" @@ -7849,7 +7849,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -7914,7 +7914,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.12.0.tgz", "integrity": "sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "picocolors": "^1.1.1", @@ -7973,14 +7973,14 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -8003,7 +8003,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/lodash.merge": { @@ -8023,7 +8023,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", @@ -8040,7 +8040,7 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz", "integrity": "sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ansi-fragments": "^0.2.1", @@ -8055,7 +8055,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8065,7 +8065,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -8077,7 +8077,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "locate-path": "^5.0.0", @@ -8091,7 +8091,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "p-locate": "^4.1.0" @@ -8104,7 +8104,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "p-try": "^2.0.0" @@ -8120,7 +8120,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "p-limit": "^2.2.0" @@ -8133,7 +8133,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -8148,14 +8148,14 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/logkitty/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "cliui": "^6.0.0", @@ -8178,7 +8178,7 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "camelcase": "^5.0.0", @@ -8228,7 +8228,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8238,7 +8238,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -8272,7 +8272,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -8597,7 +8597,7 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "mime": "cli.js" @@ -8610,7 +8610,7 @@ "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -8641,7 +8641,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8720,7 +8720,7 @@ "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -8730,7 +8730,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/nocache/-/nocache-3.0.4.tgz", "integrity": "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -8752,7 +8752,7 @@ "version": "1.15.0", "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -8775,7 +8775,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.0.0" @@ -8815,7 +8815,7 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8925,7 +8925,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -8944,7 +8944,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" @@ -8960,7 +8960,7 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-wsl": "^1.1.0" @@ -8991,7 +8991,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "bl": "^4.1.0", @@ -9065,7 +9065,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" @@ -9081,7 +9081,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" @@ -9106,7 +9106,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -9119,7 +9119,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", @@ -9174,7 +9174,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/path-type": { @@ -9309,7 +9309,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "kleur": "^3.0.3", @@ -9350,7 +9350,7 @@ "version": "6.14.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -9393,7 +9393,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -9423,7 +9423,7 @@ "version": "2.5.3", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -9956,7 +9956,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -10083,14 +10083,14 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.1", @@ -10111,7 +10111,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -10121,7 +10121,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "onetime": "^5.1.0", @@ -10135,7 +10135,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -10212,7 +10212,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -10256,7 +10256,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -10312,7 +10312,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/scheduler": { @@ -10436,7 +10436,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/set-function-length": { @@ -10540,7 +10540,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -10560,7 +10560,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -10577,7 +10577,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -10596,7 +10596,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -10637,7 +10637,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/slash": { @@ -10653,7 +10653,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^3.2.0", @@ -10668,7 +10668,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^1.9.0" @@ -10681,7 +10681,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "color-name": "1.1.3" @@ -10691,7 +10691,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/smol-toml": { @@ -10834,7 +10834,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -10984,7 +10984,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -11007,7 +11007,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -11032,7 +11032,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -11234,7 +11234,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "media-typer": "0.3.0", @@ -11405,7 +11405,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 4.0.0" @@ -11488,7 +11488,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/utils-merge": { @@ -11504,7 +11504,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -11545,7 +11545,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "defaults": "^1.0.3" @@ -11643,7 +11643,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/which-typed-array": { @@ -11718,7 +11718,7 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" @@ -11785,7 +11785,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=10" diff --git a/examples/react-native/RunAnywhereAI/react-native.config.js b/examples/react-native/RunAnywhereAI/react-native.config.js index 760887e4d..ea74cd30f 100644 --- a/examples/react-native/RunAnywhereAI/react-native.config.js +++ b/examples/react-native/RunAnywhereAI/react-native.config.js @@ -8,7 +8,10 @@ module.exports = { }, }, dependencies: { - // Disable audio libraries on iOS - they're incompatible with New Architecture + // Disable autolinking for audio libraries that are incompatible with New Architecture. + // iOS: These libraries don't support the New Architecture (TurboModules/Fabric). + // Android: Some libraries also disabled on Android due to build conflicts or + // because we use custom native implementations instead. 'react-native-live-audio-stream': { platforms: { ios: null, @@ -17,12 +20,13 @@ module.exports = { 'react-native-audio-recorder-player': { platforms: { ios: null, - android: null, + android: null, // Disabled on both platforms - using custom audio implementation }, }, 'react-native-sound': { platforms: { ios: null, + android: null, // Disabled on both platforms - using custom audio implementation }, }, 'react-native-tts': { diff --git a/examples/react-native/RunAnywhereAI/src/components/chat/MessageBubble.tsx b/examples/react-native/RunAnywhereAI/src/components/chat/MessageBubble.tsx index 99657c112..1a011580f 100644 --- a/examples/react-native/RunAnywhereAI/src/components/chat/MessageBubble.tsx +++ b/examples/react-native/RunAnywhereAI/src/components/chat/MessageBubble.tsx @@ -20,6 +20,7 @@ import { Typography } from '../../theme/typography'; import { Spacing, BorderRadius, Padding, Layout } from '../../theme/spacing'; import type { Message } from '../../types/chat'; import { MessageRole } from '../../types/chat'; +import { ToolCallIndicator } from './ToolCallIndicator'; interface MessageBubbleProps { message: Message; @@ -90,6 +91,11 @@ export const MessageBubble: React.FC = ({ )} + {/* Tool Call Indicator (for messages that used tools) */} + {isAssistant && message.toolCallInfo && ( + + )} + {/* Thinking Section (expandable) */} {hasThinking && ( = ({ + toolCallInfo, +}) => { + const [showSheet, setShowSheet] = useState(false); + + const backgroundColor = toolCallInfo.success + ? Colors.primaryBlue + '1A' // 10% opacity + : Colors.primaryOrange + '1A'; + + const borderColor = toolCallInfo.success + ? Colors.primaryBlue + '4D' // 30% opacity + : Colors.primaryOrange + '4D'; + + const iconColor = toolCallInfo.success + ? Colors.primaryBlue + : Colors.primaryOrange; + + return ( + <> + setShowSheet(true)} + activeOpacity={0.7} + > + + {toolCallInfo.toolName} + + + setShowSheet(false)} + /> + + ); +}; + +interface ToolCallDetailSheetProps { + visible: boolean; + toolCallInfo: ToolCallInfo; + onClose: () => void; +} + +/** + * Full detail sheet showing tool call arguments and results as JSON + */ +const ToolCallDetailSheet: React.FC = ({ + visible, + toolCallInfo, + onClose, +}) => { + return ( + + + {/* Header */} + + Tool Call + + Done + + + + + {/* Status Section */} + + + + {toolCallInfo.success ? 'Success' : 'Failed'} + + + + {/* Tool Name */} + + + {/* Arguments */} + + + {/* Result (if available) */} + {toolCallInfo.result && ( + + )} + + {/* Error (if available) */} + {toolCallInfo.error && ( + + )} + + + + ); +}; + +interface DetailSectionProps { + title: string; + content: string; + isError?: boolean; +} + +const DetailSection: React.FC = ({ + title, + content, + isError = false, +}) => ( + + {title} + + {content} + + +); + +interface CodeSectionProps { + title: string; + code: string; +} + +const CodeSection: React.FC = ({ title, code }) => { + // Try to pretty print JSON + let formattedCode = code; + try { + const parsed = JSON.parse(code); + formattedCode = JSON.stringify(parsed, null, 2); + } catch { + // Keep original if not valid JSON + } + + return ( + + {title} + + {formattedCode} + + + ); +}; + +/** + * Badge shown in chat to indicate tool calling is enabled + * Matches iOS toolCallingBadge + */ +interface ToolCallingBadgeProps { + toolCount: number; +} + +export const ToolCallingBadge: React.FC = ({ + toolCount, +}) => { + return ( + + + + Tools enabled ({toolCount}) + + + ); +}; + +const styles = StyleSheet.create({ + // Badge styles + badge: { + flexDirection: 'row', + alignItems: 'center', + gap: 6, + paddingHorizontal: 10, + paddingVertical: 6, + borderRadius: 8, + marginBottom: Spacing.small, + alignSelf: 'flex-start', + }, + badgeText: { + ...Typography.caption2, + color: Colors.textSecondary, + }, + + // Sheet styles + sheetContainer: { + flex: 1, + backgroundColor: Colors.backgroundPrimary, + }, + sheetHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingHorizontal: Padding.padding16, + paddingVertical: Padding.padding12, + borderBottomWidth: 1, + borderBottomColor: Colors.borderLight, + }, + sheetTitle: { + ...Typography.headline, + color: Colors.textPrimary, + }, + closeButton: { + paddingHorizontal: Padding.padding12, + paddingVertical: Padding.padding8, + }, + closeButtonText: { + ...Typography.body, + color: Colors.primaryBlue, + fontWeight: '600', + }, + sheetContent: { + flex: 1, + }, + sheetContentContainer: { + padding: Padding.padding16, + gap: 20, + }, + + // Status section + statusSection: { + flexDirection: 'row', + alignItems: 'center', + gap: 10, + padding: Padding.padding16, + borderRadius: BorderRadius.regular, + }, + statusText: { + ...Typography.headline, + color: Colors.textPrimary, + }, + + // Detail section + section: { + gap: Spacing.small, + }, + sectionTitle: { + ...Typography.caption, + color: Colors.textSecondary, + }, + sectionContent: { + ...Typography.body, + color: Colors.textPrimary, + }, + errorText: { + color: Colors.statusRed, + }, + + // Code section + codeContainer: { + backgroundColor: Colors.backgroundSecondary, + borderRadius: BorderRadius.regular, + padding: Padding.padding12, + }, + codeText: { + ...Typography.footnote, + fontFamily: 'Menlo', + color: Colors.textPrimary, + }, + + // Tool calling badge (above input) + toolCallingBadge: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 6, + paddingHorizontal: Padding.padding12, + paddingVertical: Padding.padding8, + backgroundColor: Colors.primaryBlue + '1A', + borderTopWidth: 1, + borderTopColor: Colors.borderLight, + }, + toolCallingBadgeText: { + ...Typography.caption, + color: Colors.primaryBlue, + fontWeight: '500', + }, +}); + +export default ToolCallIndicator; diff --git a/examples/react-native/RunAnywhereAI/src/components/chat/index.ts b/examples/react-native/RunAnywhereAI/src/components/chat/index.ts index aa4f443d9..02ba26c88 100644 --- a/examples/react-native/RunAnywhereAI/src/components/chat/index.ts +++ b/examples/react-native/RunAnywhereAI/src/components/chat/index.ts @@ -5,3 +5,4 @@ export { MessageBubble } from './MessageBubble'; export { TypingIndicator } from './TypingIndicator'; export { ChatInput } from './ChatInput'; +export { ToolCallIndicator, ToolCallingBadge } from './ToolCallIndicator'; diff --git a/examples/react-native/RunAnywhereAI/src/navigation/TabNavigator.tsx b/examples/react-native/RunAnywhereAI/src/navigation/TabNavigator.tsx index b4354430b..62c9b2e29 100644 --- a/examples/react-native/RunAnywhereAI/src/navigation/TabNavigator.tsx +++ b/examples/react-native/RunAnywhereAI/src/navigation/TabNavigator.tsx @@ -6,7 +6,7 @@ * - STT (Speech-to-Text) * - TTS (Text-to-Speech) * - Voice (Voice Assistant - STT + LLM + TTS) - * - Settings + * - Settings (includes Tool Settings) */ import React from 'react'; @@ -108,7 +108,7 @@ export const TabNavigator: React.FC = () => { component={VoiceAssistantScreen} options={{ tabBarLabel: tabLabels.Voice }} /> - {/* Tab 4: Settings */} + {/* Tab 4: Settings (includes Tool Settings) */} Math.random().toString(36).substring(2, 15); +// ============================================================================= +// TOOL CALLING SETUP - Weather API Example +// ============================================================================= + +/** + * Register tools for the chat. This enables the LLM to call external APIs. + * Users just chat normally - tool calls happen transparently. + */ +const registerChatTools = () => { + // Clear any existing tools + RunAnywhere.clearTools(); + + // Weather tool - Real API (wttr.in - no key needed) + RunAnywhere.registerTool( + { + name: 'get_weather', + description: 'Gets the current weather for a city or location', + parameters: [ + { + name: 'location', + type: 'string', + description: 'City name or location (e.g., "Tokyo", "New York", "London")', + required: true, + }, + ], + }, + async (args) => { + // Handle both 'location' and 'city' parameter names (models vary) + const location = (args.location || args.city) as string; + console.log('[Tool] get_weather called for:', location); + + try { + const url = `https://wttr.in/${encodeURIComponent(location)}?format=j1`; + const response = await fetch(url); + + if (!response.ok) { + return { error: `Weather API error: ${response.status}` }; + } + + const data = await response.json(); + const current = data.current_condition[0]; + const area = data.nearest_area?.[0]; + + return { + location: area?.areaName?.[0]?.value || location, + country: area?.country?.[0]?.value || '', + temperature_f: parseInt(current.temp_F, 10), + temperature_c: parseInt(current.temp_C, 10), + condition: current.weatherDesc[0].value, + humidity: `${current.humidity}%`, + wind_mph: `${current.windspeedMiles} mph`, + feels_like_f: parseInt(current.FeelsLikeF, 10), + }; + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + console.error('[Tool] Weather fetch failed:', msg); + return { error: msg }; + } + } + ); + + // Current time tool + RunAnywhere.registerTool( + { + name: 'get_current_time', + description: 'Gets the current date and time', + parameters: [], + }, + async () => { + console.log('[Tool] get_current_time called'); + const now = new Date(); + return { + date: now.toLocaleDateString(), + time: now.toLocaleTimeString(), + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + }; + } + ); + + // Calculator tool - Math evaluation + RunAnywhere.registerTool( + { + name: 'calculate', + description: 'Performs math calculations. Supports +, -, *, /, and parentheses', + parameters: [ + { + name: 'expression', + type: 'string', + description: 'Math expression (e.g., "2 + 2 * 3", "(10 + 5) / 3")', + required: true, + }, + ], + }, + async (args) => { + const expression = (args.expression || args.input) as string; + console.log('[Tool] calculate called for:', expression); + try { + // Safe math evaluation using recursive descent parser + const result = safeEvaluateExpression(expression); + return { + expression: expression, + result: result, + }; + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + return { error: `Failed to calculate: ${msg}` }; + } + } + ); + + console.log('[ChatScreen] Tools registered: get_weather, get_current_time, calculate'); +}; + +/** + * Detect tool call format based on model ID and name + * LFM2-Tool models use Pythonic format, others use JSON format + * + * Matches iOS: LLMViewModel+ToolCalling.swift detectToolCallFormat() + * Checks both ID and name since model might be identified by either + */ +const detectToolCallFormat = (modelId: string | undefined, modelName: string | undefined): string => { + // Check model ID first (more reliable - e.g., "lfm2-1.2b-tool-q4_k_m") + if (modelId) { + const id = modelId.toLowerCase(); + if (id.includes('lfm2') && id.includes('tool')) { + return 'lfm2'; + } + } + + // Also check model name (e.g., "LiquidAI LFM2 1.2B Tool Q4_K_M") + if (modelName) { + const name = modelName.toLowerCase(); + if (name.includes('lfm2') && name.includes('tool')) { + return 'lfm2'; + } + } + + // Default JSON format for general-purpose models + return 'default'; +}; + export const ChatScreen: React.FC = () => { // Conversation store const { @@ -77,6 +219,7 @@ export const ChatScreen: React.FC = () => { const [showConversationList, setShowConversationList] = useState(false); const [showModelSelection, setShowModelSelection] = useState(false); const [isInitialized, setIsInitialized] = useState(false); + const [registeredToolCount, setRegisteredToolCount] = useState(0); // Refs const flatListRef = useRef(null); @@ -143,15 +286,19 @@ export const ChatScreen: React.FC = () => { /** * Check if a model is loaded + * Note: If a model is already loaded from a previous session, we set a placeholder. + * For proper tool calling format detection, the user should select a model through the UI. */ const checkModelStatus = async () => { try { const isLoaded = await RunAnywhere.isModelLoaded(); console.warn('[ChatScreen] Text model loaded:', isLoaded); if (isLoaded) { + // Model is loaded but we don't know which one - set placeholder + // User should select a model through UI for proper format detection setCurrentModel({ id: 'loaded-model', - name: 'Loaded Model', + name: 'Loaded Model (select model for tool calling)', category: ModelCategory.Language, compatibleFrameworks: [LLMFramework.LlamaCpp], preferredFramework: LLMFramework.LlamaCpp, @@ -159,6 +306,11 @@ export const ChatScreen: React.FC = () => { isAvailable: true, supportsThinking: false, }); + // Register tools if model already loaded + registerChatTools(); + const tools = RunAnywhere.getRegisteredTools(); + setRegisteredToolCount(tools.length); + console.warn('[ChatScreen] Model loaded from previous session. For LFM2 tool calling, please select the model again.'); } } catch (error) { console.warn('[ChatScreen] Error checking model status:', error); @@ -203,7 +355,8 @@ export const ChatScreen: React.FC = () => { const success = await RunAnywhere.loadModel(model.localPath); if (success) { - setCurrentModel({ + // Set the model info with actual ID and name for format detection + const modelInfo = { id: model.id, name: model.name, category: ModelCategory.Language, @@ -212,8 +365,18 @@ export const ChatScreen: React.FC = () => { isDownloaded: true, isAvailable: true, supportsThinking: false, - }); - console.warn('[ChatScreen] Model loaded successfully'); + }; + setCurrentModel(modelInfo); + + // Log model info for format detection debugging + const format = detectToolCallFormat(model.id, model.name); + console.warn(`[ChatScreen] Model loaded: id="${model.id}", name="${model.name}", detected format="${format}"`); + + // Register tools when model loads + registerChatTools(); + const tools = RunAnywhere.getRegisteredTools(); + setRegisteredToolCount(tools.length); + console.warn('[ChatScreen] Tools registered:', tools.length, 'tools'); } else { const lastError = await RunAnywhere.getLastError(); Alert.alert( @@ -230,8 +393,11 @@ export const ChatScreen: React.FC = () => { }; /** - * Send a message using the real SDK with streaming (matches Swift SDK) - * Uses RunAnywhere.generateStream() for real-time token display + * Send a message using the real SDK with tool calling support + * Uses RunAnywhere.generateWithTools() for AI that can take actions + * + * Example: "What's the weather in Tokyo?" + * โ†’ LLM calls get_weather tool โ†’ Real API call โ†’ Final response */ const handleSend = useCallback(async () => { if (!inputText.trim() || !currentConversation) return; @@ -249,12 +415,12 @@ export const ChatScreen: React.FC = () => { setInputText(''); setIsLoading(true); - // Create placeholder assistant message for streaming + // Create placeholder assistant message const assistantMessageId = generateId(); const assistantMessage: Message = { id: assistantMessageId, role: MessageRole.Assistant, - content: '', + content: 'Thinking...', timestamp: new Date(), }; await addMessage(assistantMessage, currentConversation.id); @@ -265,55 +431,66 @@ export const ChatScreen: React.FC = () => { }, 100); try { - console.log('[ChatScreen] Starting streaming generation for:', prompt); - - // Use streaming generation (matches Swift SDK: RunAnywhere.generateStream) - const streamingResult = await RunAnywhere.generateStream(prompt, { + // Detect tool call format based on loaded model (matches iOS LLMViewModel+ToolCalling.swift) + const format = detectToolCallFormat(currentModel?.id, currentModel?.name); + console.log('[ChatScreen] Starting generation with tools for:', prompt, 'model:', currentModel?.id, 'format:', format); + + // Use tool-enabled generation + // If the LLM needs to call a tool (like weather API), it happens automatically + const result = await RunAnywhere.generateWithTools(prompt, { + autoExecute: true, + maxToolCalls: 3, maxTokens: 1000, temperature: 0.7, + format: format, }); - let fullResponse = ''; + // Log tool usage for debugging + if (result.toolCalls.length > 0) { + console.log('[ChatScreen] Tools used:', result.toolCalls.map(t => t.toolName)); + console.log('[ChatScreen] Tool results:', result.toolResults); + } + + // Build final message content + let finalContent = result.text || '(No response generated)'; - // Stream tokens in real-time (matches Swift's for await loop) - for await (const token of streamingResult.stream) { - fullResponse += token; + // Extract tool call info from result (matching iOS implementation) + let toolCallInfo: ToolCallInfo | undefined; + if (result.toolCalls.length > 0) { + const lastToolCall = result.toolCalls[result.toolCalls.length - 1]; + const lastToolResult = result.toolResults[result.toolResults.length - 1]; - // Update assistant message content as tokens arrive - updateMessage( - { - ...assistantMessage, - content: fullResponse, - }, - currentConversation.id - ); - - // Scroll to keep up with new content - flatListRef.current?.scrollToEnd({ animated: false }); + toolCallInfo = { + toolName: lastToolCall.toolName, + arguments: JSON.stringify(lastToolCall.arguments, null, 2), + result: lastToolResult?.success + ? JSON.stringify(lastToolResult.result, null, 2) + : undefined, + success: lastToolResult?.success ?? false, + error: lastToolResult?.error, + }; + + console.log('[ChatScreen] Created toolCallInfo:', toolCallInfo.toolName, 'success:', toolCallInfo.success); } - // Get final metrics after streaming completes - const result = await streamingResult.result; - console.log('[ChatScreen] Streaming complete, metrics:', result); - - // Update with final message including analytics + // Update with final message const finalMessage: Message = { id: assistantMessageId, role: MessageRole.Assistant, - content: fullResponse || '(No response generated)', + content: finalContent, timestamp: new Date(), + toolCallInfo, // Attach tool call info to message modelInfo: { - modelId: result.modelUsed || 'unknown', - modelName: result.modelUsed || 'Unknown Model', - framework: result.framework || 'llama.cpp', + modelId: currentModel?.id || 'unknown', + modelName: currentModel?.name || 'Unknown Model', + framework: 'llama.cpp', frameworkDisplayName: 'llama.cpp', }, analytics: { - totalGenerationTime: result.latencyMs, - inputTokens: result.inputTokens || Math.ceil(prompt.length / 4), - outputTokens: result.tokensUsed, - averageTokensPerSecond: result.tokensPerSecond || 0, - timeToFirstToken: result.timeToFirstTokenMs, + totalGenerationTime: 0, + inputTokens: Math.ceil(prompt.length / 4), + outputTokens: Math.ceil(finalContent.length / 4), + averageTokensPerSecond: 0, completionStatus: 'completed', wasThinkingMode: false, wasInterrupted: false, @@ -335,7 +512,7 @@ export const ChatScreen: React.FC = () => { { id: assistantMessageId, role: MessageRole.Assistant, - content: `Error: ${error}\n\nThis likely means no LLM model is loaded. Use demo mode to test the interface, or provide a GGUF model.`, + content: `Error: ${error}\n\nThis likely means no LLM model is loaded. Load a model first.`, timestamp: new Date(), }, currentConversation.id @@ -343,7 +520,7 @@ export const ChatScreen: React.FC = () => { } finally { setIsLoading(false); } - }, [inputText, currentConversation, addMessage, updateMessage]); + }, [inputText, currentConversation, currentModel, addMessage, updateMessage]); /** * Create a new conversation (clears current chat) @@ -508,6 +685,11 @@ export const ChatScreen: React.FC = () => { {/* Typing Indicator */} {isLoading && } + {/* Tool Calling Badge (shows when tools are enabled) */} + {currentModel && registeredToolCount > 0 && ( + + )} + {/* Input Area */} { >({}); const [downloadedModels, setDownloadedModels] = useState([]); + // Tool calling state + const [toolCallingEnabled, setToolCallingEnabled] = useState(false); + const [registeredTools, setRegisteredTools] = useState}>>([]); + // Capability names mapping const capabilityNames: Record = { 0: 'STT (Speech-to-Text)', @@ -168,6 +174,7 @@ export const SettingsScreen: React.FC = () => { useEffect(() => { loadData(); loadApiConfiguration(); + loadToolCallingSettings(); }, []); /** @@ -187,6 +194,158 @@ export const SettingsScreen: React.FC = () => { } }; + /** + * Load tool calling settings from AsyncStorage + */ + const loadToolCallingSettings = async () => { + try { + const enabled = await AsyncStorage.getItem(STORAGE_KEYS.TOOL_CALLING_ENABLED); + setToolCallingEnabled(enabled === 'true'); + refreshRegisteredTools(); + } catch (error) { + console.error('[Settings] Failed to load tool calling settings:', error); + } + }; + + /** + * Refresh the list of registered tools from SDK + */ + const refreshRegisteredTools = () => { + const tools = RunAnywhere.getRegisteredTools(); + setRegisteredTools(tools.map(t => ({ + name: t.name, + description: t.description, + parameters: t.parameters || [], + }))); + }; + + /** + * Toggle tool calling enabled state + */ + const handleToggleToolCalling = async (enabled: boolean) => { + setToolCallingEnabled(enabled); + try { + await AsyncStorage.setItem(STORAGE_KEYS.TOOL_CALLING_ENABLED, enabled ? 'true' : 'false'); + } catch (error) { + console.error('[Settings] Failed to save tool calling setting:', error); + } + }; + + /** + * Register demo tools (weather, time, calculator) + */ + const registerDemoTools = () => { + // Clear existing tools + RunAnywhere.clearTools(); + + // Weather tool - Real API (wttr.in - no key needed) + RunAnywhere.registerTool( + { + name: 'get_weather', + description: 'Gets the current weather for a city or location', + parameters: [ + { + name: 'location', + type: 'string', + description: 'City name or location (e.g., "Tokyo", "New York", "London")', + required: true, + }, + ], + }, + async (args: Record) => { + const location = (args.location as string) || 'San Francisco'; + try { + const response = await fetch( + `https://wttr.in/${encodeURIComponent(location)}?format=j1` + ); + const data = await response.json(); + const current = data.current_condition?.[0]; + return { + location, + temperature_c: current?.temp_C || 'N/A', + temperature_f: current?.temp_F || 'N/A', + condition: current?.weatherDesc?.[0]?.value || 'Unknown', + humidity: current?.humidity || 'N/A', + wind_kph: current?.windspeedKmph || 'N/A', + }; + } catch (error) { + return { error: `Failed to get weather: ${error}` }; + } + } + ); + + // Time tool - Real system time + RunAnywhere.registerTool( + { + name: 'get_current_time', + description: 'Gets the current date, time, and timezone information', + parameters: [], + }, + async () => { + const now = new Date(); + return { + datetime: now.toLocaleString(), + time: now.toLocaleTimeString(), + timestamp: now.toISOString(), + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + }; + } + ); + + // Calculator tool - Math evaluation + RunAnywhere.registerTool( + { + name: 'calculate', + description: 'Performs math calculations. Supports +, -, *, /, and parentheses', + parameters: [ + { + name: 'expression', + type: 'string', + description: 'Math expression (e.g., "2 + 2 * 3", "(10 + 5) / 3")', + required: true, + }, + ], + }, + async (args: Record) => { + const expression = (args.expression as string) || '0'; + try { + // Safe math evaluation using recursive descent parser + const result = safeEvaluateExpression(expression); + return { + expression: expression, + result: result, + }; + } catch (error) { + return { error: `Failed to calculate: ${error}` }; + } + } + ); + + refreshRegisteredTools(); + Alert.alert('Demo Tools Added', '3 demo tools have been registered: get_weather, get_current_time, calculate'); + }; + + /** + * Clear all registered tools + */ + const clearAllTools = () => { + Alert.alert( + 'Clear All Tools', + 'Are you sure you want to remove all registered tools?', + [ + { text: 'Cancel', style: 'cancel' }, + { + text: 'Clear', + style: 'destructive', + onPress: () => { + RunAnywhere.clearTools(); + refreshRegisteredTools(); + }, + }, + ] + ); + }; + /** * Normalize base URL by adding https:// if no scheme is present */ @@ -801,6 +960,102 @@ export const SettingsScreen: React.FC = () => { + {/* Tool Settings - Matches iOS ToolSettingsView */} + {renderSectionHeader('Tool Settings')} + + {/* Enable Tool Calling Toggle */} + + + Enable Tool Calling + + Allow LLMs to call tools (APIs, functions) + + + handleToggleToolCalling(!toolCallingEnabled)} + > + + + + + {toolCallingEnabled && ( + <> + + + {/* Registered Tools Count */} + + Registered Tools + 0 ? Colors.primaryGreen : Colors.textSecondary }]}> + {registeredTools.length} {registeredTools.length === 1 ? 'tool' : 'tools'} + + + + {/* Demo Tools Button */} + {registeredTools.length === 0 && ( + <> + + + + Add Demo Tools + + + )} + + {/* Registered Tools List */} + {registeredTools.length > 0 && ( + <> + + {registeredTools.map((tool, index) => ( + + + + {tool.name} + {tool.description} + {tool.parameters.length > 0 && ( + + {tool.parameters.map((p) => ( + + {p.name} + + ))} + + )} + + {index < registeredTools.length - 1 && } + + ))} + + {/* Clear All Tools Button */} + + + + Clear All Tools + + + )} + + )} + + + Tools allow the LLM to call external APIs and functions to get real-time data. + + + {/* Storage Overview - Matches iOS CombinedSettingsView */} {renderSectionHeader('Storage Overview')} @@ -1520,6 +1775,116 @@ const styles = StyleSheet.create({ modalButtonTextDisabled: { color: Colors.textTertiary, }, + // Tool Settings styles + toolSettingRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + padding: Padding.padding16, + }, + toolSettingInfo: { + flex: 1, + marginRight: Spacing.medium, + }, + toolSettingLabel: { + ...Typography.body, + color: Colors.textPrimary, + fontWeight: '500', + }, + toolSettingDescription: { + ...Typography.footnote, + color: Colors.textSecondary, + marginTop: Spacing.xxSmall, + }, + toolSettingValue: { + ...Typography.body, + fontWeight: '600', + }, + toggleButton: { + width: 51, + height: 31, + borderRadius: 15.5, + backgroundColor: Colors.backgroundGray5, + padding: 2, + justifyContent: 'center', + }, + toggleButtonActive: { + backgroundColor: Colors.primaryBlue, + }, + toggleKnob: { + width: 27, + height: 27, + borderRadius: 13.5, + backgroundColor: Colors.textWhite, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.2, + shadowRadius: 2, + elevation: 2, + }, + toggleKnobActive: { + alignSelf: 'flex-end', + }, + demoToolsButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: Spacing.small, + padding: Padding.padding16, + }, + demoToolsButtonText: { + ...Typography.body, + color: Colors.primaryBlue, + fontWeight: '600', + }, + toolRow: { + flexDirection: 'row', + alignItems: 'flex-start', + padding: Padding.padding16, + gap: Spacing.medium, + }, + toolInfo: { + flex: 1, + }, + toolName: { + ...Typography.subheadline, + color: Colors.textPrimary, + fontWeight: '600', + }, + toolDescription: { + ...Typography.footnote, + color: Colors.textSecondary, + marginTop: Spacing.xxSmall, + }, + toolParams: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: Spacing.xSmall, + marginTop: Spacing.small, + }, + toolParamChip: { + backgroundColor: Colors.badgeBlue, + paddingHorizontal: Spacing.small, + paddingVertical: Spacing.xxSmall, + borderRadius: BorderRadius.small, + }, + toolParamText: { + ...Typography.caption2, + color: Colors.primaryBlue, + fontWeight: '500', + }, + clearToolsButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: Spacing.small, + padding: Padding.padding16, + }, + clearToolsButtonText: { + ...Typography.body, + color: Colors.statusRed, + fontWeight: '600', + }, }); export default SettingsScreen; diff --git a/examples/react-native/RunAnywhereAI/src/types/chat.ts b/examples/react-native/RunAnywhereAI/src/types/chat.ts index 9fca6f10b..a0106afe6 100644 --- a/examples/react-native/RunAnywhereAI/src/types/chat.ts +++ b/examples/react-native/RunAnywhereAI/src/types/chat.ts @@ -85,6 +85,27 @@ export interface MessageModelInfo { frameworkDisplayName: string; } +/** + * Tool call information attached to a message + * Matches iOS ToolCallInfo + */ +export interface ToolCallInfo { + /** Name of the tool that was called */ + toolName: string; + + /** Arguments passed to the tool (JSON string) */ + arguments: string; + + /** Result from the tool (JSON string, if successful) */ + result?: string; + + /** Whether the tool call was successful */ + success: boolean; + + /** Error message (if failed) */ + error?: string; +} + /** * Chat message */ @@ -110,6 +131,9 @@ export interface Message { /** Model info */ modelInfo?: MessageModelInfo; + /** Tool call info (for messages that used tools) */ + toolCallInfo?: ToolCallInfo; + /** Whether the message is still streaming */ isStreaming?: boolean; } diff --git a/examples/react-native/RunAnywhereAI/src/types/index.ts b/examples/react-native/RunAnywhereAI/src/types/index.ts index b08583cc5..780690ccc 100644 --- a/examples/react-native/RunAnywhereAI/src/types/index.ts +++ b/examples/react-native/RunAnywhereAI/src/types/index.ts @@ -22,7 +22,7 @@ export * from './settings'; // Tab 1: Speech-to-Text // Tab 2: Text-to-Speech // Tab 3: Voice Assistant (STT + LLM + TTS) -// Tab 4: Settings +// Tab 4: Settings (includes Tool Settings) export type RootTabParamList = { Chat: undefined; STT: undefined; diff --git a/examples/react-native/RunAnywhereAI/src/utils/mathParser.ts b/examples/react-native/RunAnywhereAI/src/utils/mathParser.ts new file mode 100644 index 000000000..0ed8bd231 --- /dev/null +++ b/examples/react-native/RunAnywhereAI/src/utils/mathParser.ts @@ -0,0 +1,182 @@ +/** + * Safe Math Expression Evaluator + * + * A secure alternative to eval() for math expression evaluation. + * Uses a recursive descent parser with proper operator precedence. + * + * Supports: + * - Numbers (integers and decimals) + * - Operators: +, -, *, / + * - Parentheses for grouping + * - Unary minus (negative numbers) + * + * Security: + * - No eval() or Function() usage + * - Tokenizes and validates input + * - Rejects invalid characters/expressions + * + * Example usage: + * safeEvaluateExpression("2 + 3 * 4") // Returns 14 + * safeEvaluateExpression("(2 + 3) * 4") // Returns 20 + * safeEvaluateExpression("-5 + 3") // Returns -2 + */ + +/** + * Tokenize the expression into numbers, operators, and parentheses + */ +const tokenize = (input: string): string[] => { + // Remove all whitespace + const cleaned = input.replace(/\s+/g, ''); + + // Match numbers (including decimals) and operators/parentheses + const tokens = cleaned.match(/(\d+\.?\d*|[()+\-*/])/g); + + if (!tokens) { + throw new Error('Invalid expression: no valid tokens found'); + } + + // Validate that we consumed the entire input + const reconstructed = tokens.join(''); + if (reconstructed !== cleaned) { + throw new Error('Invalid expression: contains invalid characters'); + } + + return tokens; +}; + +/** + * Safe math expression evaluator using recursive descent parsing + * + * Grammar: + * expression -> term (('+' | '-') term)* + * term -> factor (('*' | '/') factor)* + * factor -> '-' factor | '(' expression ')' | number + * + * @param input - Math expression string (e.g., "2 + 3 * 4") + * @returns The numeric result + * @throws Error if expression is invalid + */ +export const safeEvaluateExpression = (input: string): number => { + if (!input || typeof input !== 'string') { + throw new Error('Invalid expression: input must be a non-empty string'); + } + + const tokens = tokenize(input); + let pos = 0; + + /** + * Get the current token without advancing + */ + const peek = (): string | undefined => tokens[pos]; + + /** + * Consume and return the current token + */ + const consume = (): string | undefined => tokens[pos++]; + + /** + * Parse a factor: number, parenthesized expression, or unary minus + */ + const parseFactor = (): number => { + const token = peek(); + + if (token === undefined) { + throw new Error('Unexpected end of expression'); + } + + // Handle unary minus + if (token === '-') { + consume(); + return -parseFactor(); + } + + // Handle unary plus (optional, just skip it) + if (token === '+') { + consume(); + return parseFactor(); + } + + // Handle parenthesized expression + if (token === '(') { + consume(); // consume '(' + const value = parseExpression(); + if (peek() !== ')') { + throw new Error('Missing closing parenthesis'); + } + consume(); // consume ')' + return value; + } + + // Handle number + if (/^\d+\.?\d*$/.test(token)) { + consume(); + const value = parseFloat(token); + if (!Number.isFinite(value)) { + throw new Error(`Invalid number: ${token}`); + } + return value; + } + + throw new Error(`Unexpected token: ${token}`); + }; + + /** + * Parse a term: handles * and / with higher precedence + */ + const parseTerm = (): number => { + let value = parseFactor(); + + while (peek() === '*' || peek() === '/') { + const op = consume(); + const rhs = parseFactor(); + + if (op === '*') { + value *= rhs; + } else if (op === '/') { + if (rhs === 0) { + throw new Error('Division by zero'); + } + value /= rhs; + } + } + + return value; + }; + + /** + * Parse an expression: handles + and - with lower precedence + */ + const parseExpression = (): number => { + let value = parseTerm(); + + while (peek() === '+' || peek() === '-') { + const op = consume(); + const rhs = parseTerm(); + + if (op === '+') { + value += rhs; + } else if (op === '-') { + value -= rhs; + } + } + + return value; + }; + + // Parse the entire expression + const result = parseExpression(); + + // Ensure all tokens were consumed + if (pos !== tokens.length) { + throw new Error(`Unexpected token at position ${pos}: ${tokens[pos]}`); + } + + // Check for valid result + if (!Number.isFinite(result)) { + throw new Error('Result is not a finite number'); + } + + return result; +}; + +export default safeEvaluateExpression; diff --git a/sdk/runanywhere-commons/CMakeLists.txt b/sdk/runanywhere-commons/CMakeLists.txt index b958a86dd..1f5d4ad4b 100644 --- a/sdk/runanywhere-commons/CMakeLists.txt +++ b/sdk/runanywhere-commons/CMakeLists.txt @@ -171,6 +171,7 @@ set(RAC_FEATURES_SOURCES src/features/llm/streaming_metrics.cpp src/features/llm/llm_analytics.cpp src/features/llm/structured_output.cpp + src/features/llm/tool_calling.cpp # STT src/features/stt/stt_component.cpp src/features/stt/rac_stt_service.cpp diff --git a/sdk/runanywhere-commons/include/rac/features/llm/rac_tool_calling.h b/sdk/runanywhere-commons/include/rac/features/llm/rac_tool_calling.h new file mode 100644 index 000000000..a6819e069 --- /dev/null +++ b/sdk/runanywhere-commons/include/rac/features/llm/rac_tool_calling.h @@ -0,0 +1,373 @@ +/** + * @file rac_tool_calling.h + * @brief RunAnywhere Commons - Tool Calling API + * + * *** SINGLE SOURCE OF TRUTH FOR ALL TOOL CALLING LOGIC *** + * + * This header provides ALL tool calling functionality. Platform SDKs should + * ONLY call these functions - no fallback implementations allowed. + * + * Architecture: + * - C++ handles: ALL parsing, prompt formatting, JSON handling, follow-up prompts + * - Platform SDKs handle ONLY: tool registry (closures), tool execution (needs platform APIs) + * + * Supported Tool Calling Formats: + * - DEFAULT: {"tool":"name","arguments":{}} (Most general models) + * - LFM2: <|tool_call_start|>[func(arg="val")]<|tool_call_end|> (Liquid AI models) + * + * Ported from: + * - Swift: ToolCallParser.swift + * - React Native: ToolCallingBridge.cpp + */ + +#ifndef RAC_TOOL_CALLING_H +#define RAC_TOOL_CALLING_H + +#include "rac/core/rac_error.h" +#include "rac/core/rac_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// ============================================================================= +// TOOL CALLING FORMATS - Different models use different formats +// ============================================================================= + +/** + * @brief Tool calling format identifiers + * + * Different LLM models use different tool calling formats. This enum allows + * specifying which format to use for parsing and prompt generation. + */ +typedef enum rac_tool_call_format { + /** + * @brief SDK Default format: JSON + * + * Format: {"tool": "name", "arguments": {...}} + * Used by: Most general-purpose models (Llama, Qwen, Mistral, etc.) + */ + RAC_TOOL_FORMAT_DEFAULT = 0, + + /** + * @brief Liquid AI LFM2-Tool format + * + * Format: <|tool_call_start|>[func_name(arg1="val1", arg2="val2")]<|tool_call_end|> + * Used by: LiquidAI/LFM2-1.2B-Tool, LiquidAI/LFM2-350M-Tool + * Note: Uses Pythonic function call syntax + */ + RAC_TOOL_FORMAT_LFM2 = 1, + + /** Number of formats (for iteration) */ + RAC_TOOL_FORMAT_COUNT +} rac_tool_call_format_t; + +// ============================================================================= +// TYPES - Canonical definitions used by all SDKs +// ============================================================================= + +/** + * @brief Parameter types for tool arguments + */ +typedef enum rac_tool_param_type { + RAC_TOOL_PARAM_STRING = 0, + RAC_TOOL_PARAM_NUMBER = 1, + RAC_TOOL_PARAM_BOOLEAN = 2, + RAC_TOOL_PARAM_OBJECT = 3, + RAC_TOOL_PARAM_ARRAY = 4 +} rac_tool_param_type_t; + +/** + * @brief Tool parameter definition + */ +typedef struct rac_tool_parameter { + const char* name; /**< Parameter name */ + rac_tool_param_type_t type; /**< Data type */ + const char* description; /**< Human-readable description */ + rac_bool_t required; /**< Whether required */ + const char* enum_values; /**< JSON array of allowed values (can be NULL) */ +} rac_tool_parameter_t; + +/** + * @brief Tool definition + */ +typedef struct rac_tool_definition { + const char* name; /**< Unique tool name (e.g., "get_weather") */ + const char* description; /**< What the tool does */ + const rac_tool_parameter_t* parameters; /**< Array of parameters */ + size_t num_parameters; /**< Number of parameters */ + const char* category; /**< Optional category (can be NULL) */ +} rac_tool_definition_t; + +/** + * @brief Parsed tool call from LLM output + */ +typedef struct rac_tool_call { + rac_bool_t has_tool_call; /**< Whether a tool call was found */ + char* tool_name; /**< Name of tool to execute (owned, must free) */ + char* arguments_json; /**< Arguments as JSON string (owned, must free) */ + char* clean_text; /**< Text without tool call tags (owned, must free) */ + int64_t call_id; /**< Unique call ID for tracking */ + rac_tool_call_format_t format; /**< Format that was detected/used for parsing */ +} rac_tool_call_t; + +/** + * @brief Tool calling options + */ +typedef struct rac_tool_calling_options { + int32_t max_tool_calls; /**< Max tool calls per turn (default: 5) */ + rac_bool_t auto_execute; /**< Auto-execute tools (default: true) */ + float temperature; /**< Generation temperature */ + int32_t max_tokens; /**< Max tokens to generate */ + const char* system_prompt; /**< Optional system prompt */ + rac_bool_t replace_system_prompt; /**< Replace vs append tool instructions */ + rac_bool_t keep_tools_available; /**< Keep tools after first call */ + rac_tool_call_format_t format; /**< Tool calling format (default: AUTO) */ +} rac_tool_calling_options_t; + +/** + * @brief Default tool calling options + */ +#define RAC_TOOL_CALLING_OPTIONS_DEFAULT \ + { \ + 5, /* max_tool_calls */ \ + 1, /* auto_execute = true */ \ + 0.7f, /* temperature */ \ + 1024, /* max_tokens */ \ + RAC_NULL, /* system_prompt */ \ + 0, /* replace_system_prompt = false */ \ + 0, /* keep_tools_available = false */ \ + RAC_TOOL_FORMAT_DEFAULT /* format */ \ + } + +// ============================================================================= +// PARSING API - Single Source of Truth (NO FALLBACKS) +// ============================================================================= + +/** + * @brief Parse LLM output for tool calls (auto-detect format) + * + * *** THIS IS THE ONLY PARSING IMPLEMENTATION - ALL SDKS MUST USE THIS *** + * + * Auto-detects the tool calling format by checking for format-specific tags. + * Handles ALL edge cases for each format. + * + * @param llm_output Raw LLM output text + * @param out_result Output: Parsed result (caller must free with rac_tool_call_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_parse(const char* llm_output, rac_tool_call_t* out_result); + +/** + * @brief Parse LLM output for tool calls with specified format + * + * Parses using a specific format. + * + * Supported formats: + * - RAC_TOOL_FORMAT_DEFAULT: JSON + * - RAC_TOOL_FORMAT_LFM2: <|tool_call_start|>[func(args)]<|tool_call_end|> + * + * @param llm_output Raw LLM output text + * @param format Tool calling format to use + * @param out_result Output: Parsed result (caller must free with rac_tool_call_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_parse_with_format(const char* llm_output, + rac_tool_call_format_t format, + rac_tool_call_t* out_result); + +/** + * @brief Free tool call result + * @param result Result to free + */ +RAC_API void rac_tool_call_free(rac_tool_call_t* result); + +/** + * @brief Get the human-readable name of a tool calling format + * + * @param format The format to get the name for + * @return Static string with the format name (do not free) + */ +RAC_API const char* rac_tool_call_format_name(rac_tool_call_format_t format); + +/** + * @brief Detect which format is present in LLM output + * + * Checks for format-specific markers without fully parsing. + * Returns RAC_TOOL_FORMAT_DEFAULT if no recognizable format is found. + * + * @param llm_output Raw LLM output text + * @return Detected format, or RAC_TOOL_FORMAT_DEFAULT if none detected + */ +RAC_API rac_tool_call_format_t rac_tool_call_detect_format(const char* llm_output); + +/** + * @brief Convert format name string to format enum + * + * This is the SINGLE SOURCE OF TRUTH for valid format names. + * SDKs should pass strings and let C++ handle the conversion. + * + * Valid names (case-insensitive): "default", "lfm2" + * + * @param name Format name string + * @return Corresponding format enum, or RAC_TOOL_FORMAT_DEFAULT if unknown + */ +RAC_API rac_tool_call_format_t rac_tool_call_format_from_name(const char* name); + +// ============================================================================= +// PROMPT FORMATTING API - All prompt building happens here +// ============================================================================= + +/** + * @brief Format tool definitions into system prompt (default format) + * + * Creates instruction text describing available tools and expected output format. + * Uses RAC_TOOL_FORMAT_DEFAULT (JSON). + * + * @param definitions Array of tool definitions + * @param num_definitions Number of definitions + * @param out_prompt Output: Allocated prompt string (caller must free with rac_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_format_prompt(const rac_tool_definition_t* definitions, + size_t num_definitions, char** out_prompt); + +/** + * @brief Format tool definitions with specified format + * + * Creates instruction text using the specified tool calling format. + * Each format has different tag patterns and syntax instructions. + * + * @param definitions Array of tool definitions + * @param num_definitions Number of definitions + * @param format Tool calling format to use for instructions + * @param out_prompt Output: Allocated prompt string (caller must free with rac_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_format_prompt_with_format(const rac_tool_definition_t* definitions, + size_t num_definitions, + rac_tool_call_format_t format, + char** out_prompt); + +/** + * @brief Format tools from JSON array string (default format) + * + * Convenience function when tools are provided as JSON. + * + * @param tools_json JSON array of tool definitions + * @param out_prompt Output: Allocated prompt string (caller must free with rac_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_format_prompt_json(const char* tools_json, char** out_prompt); + +/** + * @brief Format tools from JSON array string with specified format + * + * @param tools_json JSON array of tool definitions + * @param format Tool calling format to use for instructions + * @param out_prompt Output: Allocated prompt string (caller must free with rac_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_format_prompt_json_with_format(const char* tools_json, + rac_tool_call_format_t format, + char** out_prompt); + +/** + * @brief Format tools from JSON array string with format specified by name + * + * *** PREFERRED API FOR SDKS - Uses string format name *** + * + * Valid format names (case-insensitive): "default", "lfm2" + * Unknown names default to "default" format. + * + * @param tools_json JSON array of tool definitions + * @param format_name Format name string (e.g., "lfm2", "default") + * @param out_prompt Output: Allocated prompt string (caller must free with rac_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_format_prompt_json_with_format_name(const char* tools_json, + const char* format_name, + char** out_prompt); + +/** + * @brief Build the initial prompt with tools and user query + * + * Combines system prompt, tool instructions, and user prompt. + * + * @param user_prompt The user's question/request + * @param tools_json JSON array of tool definitions + * @param options Tool calling options (can be NULL for defaults) + * @param out_prompt Output: Complete formatted prompt (caller must free with rac_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_build_initial_prompt(const char* user_prompt, + const char* tools_json, + const rac_tool_calling_options_t* options, + char** out_prompt); + +/** + * @brief Build follow-up prompt after tool execution + * + * Creates the prompt to continue generation after a tool was executed. + * Handles both keepToolsAvailable=true and keepToolsAvailable=false cases. + * + * @param original_user_prompt The original user prompt + * @param tools_prompt The formatted tools prompt (can be NULL if not keeping tools) + * @param tool_name Name of the tool that was executed + * @param tool_result_json JSON string of the tool result + * @param keep_tools_available Whether to include tool definitions in follow-up + * @param out_prompt Output: Follow-up prompt (caller must free with rac_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_build_followup_prompt(const char* original_user_prompt, + const char* tools_prompt, + const char* tool_name, + const char* tool_result_json, + rac_bool_t keep_tools_available, + char** out_prompt); + +// ============================================================================= +// JSON UTILITY API - All JSON handling happens here +// ============================================================================= + +/** + * @brief Normalize JSON by adding quotes around unquoted keys + * + * Handles common LLM output patterns: {tool: "name"} โ†’ {"tool": "name"} + * + * @param json_str Input JSON string + * @param out_normalized Output: Normalized JSON (caller must free with rac_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_normalize_json(const char* json_str, char** out_normalized); + +/** + * @brief Serialize tool definitions to JSON array + * + * @param definitions Array of tool definitions + * @param num_definitions Number of definitions + * @param out_json Output: JSON array string (caller must free with rac_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_definitions_to_json(const rac_tool_definition_t* definitions, + size_t num_definitions, char** out_json); + +/** + * @brief Serialize a tool result to JSON + * + * @param tool_name Name of the tool + * @param success Whether execution succeeded + * @param result_json Result data as JSON (can be NULL) + * @param error_message Error message if failed (can be NULL) + * @param out_json Output: JSON string (caller must free with rac_free) + * @return RAC_SUCCESS on success, error code otherwise + */ +RAC_API rac_result_t rac_tool_call_result_to_json(const char* tool_name, rac_bool_t success, + const char* result_json, + const char* error_message, char** out_json); + +#ifdef __cplusplus +} +#endif + +#endif /* RAC_TOOL_CALLING_H */ diff --git a/sdk/runanywhere-commons/src/backends/llamacpp/llamacpp_backend.cpp b/sdk/runanywhere-commons/src/backends/llamacpp/llamacpp_backend.cpp index 27733806b..c9d54f115 100644 --- a/sdk/runanywhere-commons/src/backends/llamacpp/llamacpp_backend.cpp +++ b/sdk/runanywhere-commons/src/backends/llamacpp/llamacpp_backend.cpp @@ -207,6 +207,72 @@ bool LlamaCppTextGeneration::load_model(const std::string& model_path, model_path_ = model_path; llama_model_params model_params = llama_model_default_params(); + + // Detect model size from filename to set appropriate GPU layers BEFORE loading + // This prevents OOM crashes on mobile devices with limited GPU memory + // Note: We use filename heuristics here because we can't know param count until after loading + std::string path_lower = model_path; + std::transform(path_lower.begin(), path_lower.end(), path_lower.begin(), ::tolower); + + int gpu_layers = -1; // Default: all layers to GPU + + // Check for large model indicators in filename using word boundary detection + // Patterns like "7b", "8b", "13b" should match at word boundaries to avoid + // false positives like "/backup7b/" or "/2017beta/" + auto is_model_size_marker = [&path_lower](const char* marker) { + size_t pos = path_lower.find(marker); + while (pos != std::string::npos) { + // Check for word boundary before (start of string, or non-alphanumeric) + bool valid_start = (pos == 0) || !std::isalnum(path_lower[pos - 1]); + // Check for word boundary after (end of string, or non-alphanumeric except digits for patterns like "7b-q4") + size_t end_pos = pos + strlen(marker); + bool valid_end = (end_pos >= path_lower.size()) || + (!std::isalpha(path_lower[end_pos]) || path_lower[end_pos] == '-' || path_lower[end_pos] == '_'); + + if (valid_start && valid_end) { + return true; + } + pos = path_lower.find(marker, pos + 1); + } + return false; + }; + + // Detect large models (7B+) that may need GPU layer limiting on mobile + // First check for config-based override (for custom-named models) + bool is_large_model = false; + if (config.contains("expected_params_billions")) { + double expected_params = config["expected_params_billions"].get(); + is_large_model = (expected_params >= 7.0); + if (is_large_model) { + LOGI("Large model detected from config (%.1fB expected params)", expected_params); + } + } + + // Fall back to filename heuristics if no config provided + if (!is_large_model) { + is_large_model = is_model_size_marker("7b") || + is_model_size_marker("8b") || + is_model_size_marker("9b") || + is_model_size_marker("13b") || + is_model_size_marker("70b"); + } + + if (is_large_model) { + // For 7B+ models on mobile: limit GPU layers to prevent OOM + // Most 7B models have 32 layers, offload ~24 to GPU, rest to CPU + gpu_layers = 24; + LOGI("Large model detected, limiting GPU layers to %d to prevent OOM", gpu_layers); + } + + // Allow user override via config + if (config.contains("gpu_layers")) { + gpu_layers = config["gpu_layers"].get(); + LOGI("Using user-provided GPU layers: %d", gpu_layers); + } + + model_params.n_gpu_layers = gpu_layers; + LOGI("Loading model with n_gpu_layers=%d", gpu_layers); + model_ = llama_model_load_from_file(model_path.c_str(), model_params); if (!model_) { @@ -217,14 +283,45 @@ bool LlamaCppTextGeneration::load_model(const std::string& model_path, int model_train_ctx = llama_model_n_ctx_train(model_); LOGI("Model training context size: %d", model_train_ctx); + // Get model parameter count to determine appropriate context size + // Large models (7B+) need smaller context on mobile to fit in memory + uint64_t n_params = llama_model_n_params(model_); + double params_billions = static_cast(n_params) / 1e9; + LOGI("Model parameters: %.2fB", params_billions); + + // Post-load verification: warn if actual param count differs from filename heuristic + bool actual_is_large = (params_billions >= 7.0); + if (actual_is_large && !is_large_model) { + LOGI("WARNING: Model has %.1fB params but filename didn't indicate large model. " + "Consider using gpu_layers config for optimal performance.", params_billions); + } else if (!actual_is_large && is_large_model) { + LOGI("NOTE: Filename suggested large model but actual params are %.1fB. " + "GPU layer limiting may be conservative.", params_billions); + } + + // Adaptive context size based on model size for mobile devices + int adaptive_max_context; + if (params_billions >= 7.0) { + // 7B+ models: use 2048 context to fit in ~6GB GPU memory + adaptive_max_context = 2048; + LOGI("Large model detected (%.1fB params), limiting context to %d for memory", params_billions, adaptive_max_context); + } else if (params_billions >= 3.0) { + // 3-7B models: use 4096 context + adaptive_max_context = 4096; + LOGI("Medium model detected (%.1fB params), limiting context to %d", params_billions, adaptive_max_context); + } else { + // Small models (<3B): can use larger context + adaptive_max_context = max_default_context_; + } + if (user_context_size > 0) { context_size_ = std::min(user_context_size, model_train_ctx); LOGI("Using user-provided context size: %d (requested: %d, model max: %d)", context_size_, user_context_size, model_train_ctx); } else { - context_size_ = std::min(model_train_ctx, max_default_context_); - LOGI("Auto-detected context size: %d (model: %d, cap: %d)", context_size_, model_train_ctx, - max_default_context_); + context_size_ = std::min({model_train_ctx, max_default_context_, adaptive_max_context}); + LOGI("Auto-detected context size: %d (model: %d, cap: %d, adaptive: %d)", context_size_, + model_train_ctx, max_default_context_, adaptive_max_context); } llama_context_params ctx_params = llama_context_default_params(); diff --git a/sdk/runanywhere-commons/src/features/llm/tool_calling.cpp b/sdk/runanywhere-commons/src/features/llm/tool_calling.cpp new file mode 100644 index 000000000..4212d4abb --- /dev/null +++ b/sdk/runanywhere-commons/src/features/llm/tool_calling.cpp @@ -0,0 +1,1667 @@ +/** + * @file tool_calling.cpp + * @brief RunAnywhere Commons - Tool Calling Implementation + * + * *** SINGLE SOURCE OF TRUTH FOR ALL TOOL CALLING LOGIC *** + * + * This implementation consolidates all tool calling logic from: + * - Swift: ToolCallParser.swift + * - React Native: ToolCallingBridge.cpp + * + * NO FALLBACKS - All SDKs must use these functions exclusively. + * + * Supported formats: + * - DEFAULT: {"tool":"name","arguments":{}} (Most general models) + * - LFM2: <|tool_call_start|>[func(arg="val")]<|tool_call_end|> (Liquid AI models) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rac/core/rac_logger.h" +#include "rac/features/llm/rac_tool_calling.h" + +// ============================================================================= +// CONSTANTS - Format-specific tags +// ============================================================================= + +// Format: DEFAULT (JSON) +static const char* TAG_DEFAULT_START = ""; +static const char* TAG_DEFAULT_END = ""; + +// Format: LFM2 (Liquid AI) +static const char* TAG_LFM2_START = "<|tool_call_start|>"; +static const char* TAG_LFM2_END = "<|tool_call_end|>"; + +// Format names for logging/display +static const char* FORMAT_NAMES[] = { + "Default", // RAC_TOOL_FORMAT_DEFAULT + "LFM2 (Liquid)", // RAC_TOOL_FORMAT_LFM2 +}; + +// Legacy alias for backward compatibility +static const char* TOOL_CALL_START_TAG = TAG_DEFAULT_START; +static const char* TOOL_CALL_END_TAG = TAG_DEFAULT_END; + +// Standard keys for tool name (case-insensitive matching) +static const char* TOOL_NAME_KEYS[] = {"tool", "name", "function", "func", "method", + "action", "command", nullptr}; + +// Standard keys for arguments (case-insensitive matching) +static const char* ARGUMENT_KEYS[] = {"arguments", "args", "params", "parameters", "input", nullptr}; + +// ============================================================================= +// FORMAT DETECTION AND NAMING +// ============================================================================= + +extern "C" const char* rac_tool_call_format_name(rac_tool_call_format_t format) { + if (format >= 0 && format < RAC_TOOL_FORMAT_COUNT) { + return FORMAT_NAMES[format]; + } + return "Unknown"; +} + +extern "C" rac_tool_call_format_t rac_tool_call_format_from_name(const char* name) { + if (!name) { + return RAC_TOOL_FORMAT_DEFAULT; + } + + // Case-insensitive comparison + std::string name_lower(name); + for (char& c : name_lower) { + c = static_cast(tolower(c)); + } + + if (name_lower == "default") { + return RAC_TOOL_FORMAT_DEFAULT; + } else if (name_lower == "lfm2" || name_lower == "lfm" || name_lower == "liquid") { + return RAC_TOOL_FORMAT_LFM2; + } + + // Unknown format - default to DEFAULT + RAC_LOG_WARNING("ToolCalling", "Unknown tool call format name: '%s', using default", name); + return RAC_TOOL_FORMAT_DEFAULT; +} + +extern "C" rac_tool_call_format_t rac_tool_call_detect_format(const char* llm_output) { + if (!llm_output) { + return RAC_TOOL_FORMAT_DEFAULT; + } + + // Check for each format's start tag + // Order matters - check more specific formats first + + // Check LFM2 format: <|tool_call_start|> + if (strstr(llm_output, TAG_LFM2_START) != nullptr) { + return RAC_TOOL_FORMAT_LFM2; + } + + // Check Default format: + if (strstr(llm_output, TAG_DEFAULT_START) != nullptr) { + return RAC_TOOL_FORMAT_DEFAULT; + } + + // No recognizable format detected - return DEFAULT + return RAC_TOOL_FORMAT_DEFAULT; +} + +// ============================================================================= +// HELPER FUNCTIONS - String Operations +// ============================================================================= + +/** + * @brief Case-insensitive string comparison + */ +static bool str_equals_ignore_case(const char* a, const char* b) { + if (!a || !b) + return false; + while (*a && *b) { + char ca = (*a >= 'A' && *a <= 'Z') ? (*a + 32) : *a; + char cb = (*b >= 'A' && *b <= 'Z') ? (*b + 32) : *b; + if (ca != cb) + return false; + a++; + b++; + } + return *a == *b; +} + +/** + * @brief Trim whitespace from beginning and end + */ +static void trim_whitespace(const char* str, size_t len, size_t* out_start, size_t* out_end) { + size_t start = 0; + size_t end = len; + + while (start < len && (str[start] == ' ' || str[start] == '\t' || str[start] == '\n' || + str[start] == '\r')) { + start++; + } + + while (end > start && (str[end - 1] == ' ' || str[end - 1] == '\t' || str[end - 1] == '\n' || + str[end - 1] == '\r')) { + end--; + } + + *out_start = start; + *out_end = end; +} + +/** + * @brief Find substring in string + */ +static const char* find_str(const char* haystack, const char* needle) { + return strstr(haystack, needle); +} + +/** + * @brief Check if character is a key character (alphanumeric or underscore) + */ +static bool is_key_char(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; +} + +// ============================================================================= +// JSON PARSING HELPERS (Manual - No External Library) +// ============================================================================= + +/** + * @brief Find matching closing brace for JSON object + * + * Tracks string boundaries to ignore braces inside strings. + * + * @param str String to search + * @param start_pos Position of opening brace '{' + * @param out_end Output: Position of matching closing brace '}' + * @return true if found, false otherwise + */ +static bool find_matching_brace(const char* str, size_t start_pos, size_t* out_end) { + if (!str || str[start_pos] != '{') { + return false; + } + + size_t len = strlen(str); + int depth = 0; + bool in_string = false; + bool escaped = false; + + for (size_t i = start_pos; i < len; i++) { + char ch = str[i]; + + if (escaped) { + escaped = false; + continue; + } + + if (ch == '\\') { + escaped = true; + continue; + } + + if (ch == '"') { + in_string = !in_string; + continue; + } + + if (!in_string) { + if (ch == '{') { + depth++; + } else if (ch == '}') { + depth--; + if (depth == 0) { + *out_end = i; + return true; + } + } + } + } + + return false; +} + +/** + * @brief Skip whitespace in string + */ +static size_t skip_whitespace(const char* str, size_t pos, size_t len) { + while (pos < len && (str[pos] == ' ' || str[pos] == '\t' || str[pos] == '\n' || str[pos] == '\r')) { + pos++; + } + return pos; +} + +/** + * @brief Extract a JSON string value starting at the given position (must be after opening quote) + * + * @param str Input string + * @param pos Position after opening quote + * @param len Length of input string + * @param out_value Output: Allocated string value (caller must free) + * @param out_end_pos Output: Position after closing quote + * @return true if successful + */ +static bool extract_json_string(const char* str, size_t pos, size_t len, char** out_value, + size_t* out_end_pos) { + std::string result; + bool escaped = false; + + for (size_t i = pos; i < len; i++) { + char ch = str[i]; + + if (escaped) { + switch (ch) { + case 'n': + result += '\n'; + break; + case 'r': + result += '\r'; + break; + case 't': + result += '\t'; + break; + case '\\': + result += '\\'; + break; + case '"': + result += '"'; + break; + default: + result += ch; + break; + } + escaped = false; + continue; + } + + if (ch == '\\') { + escaped = true; + continue; + } + + if (ch == '"') { + // End of string + *out_value = static_cast(malloc(result.size() + 1)); + if (*out_value) { + memcpy(*out_value, result.c_str(), result.size() + 1); + } + *out_end_pos = i + 1; + return true; + } + + result += ch; + } + + return false; +} + +/** + * @brief Extract a JSON object as a raw string (including braces) + */ +static bool extract_json_object_raw(const char* str, size_t pos, size_t len, char** out_value, + size_t* out_end_pos) { + if (str[pos] != '{') { + return false; + } + + size_t end_brace; + if (!find_matching_brace(str, pos, &end_brace)) { + return false; + } + + size_t obj_len = end_brace - pos + 1; + *out_value = static_cast(malloc(obj_len + 1)); + if (!*out_value) { + return false; + } + + memcpy(*out_value, str + pos, obj_len); + (*out_value)[obj_len] = '\0'; + *out_end_pos = end_brace + 1; + return true; +} + +/** + * @brief Simple JSON key-value extractor + * + * Extracts a string or object value for a given key from a JSON object string. + * + * @param json_obj JSON object string (must include braces) + * @param key Key to find (case-insensitive) + * @param out_value Output: Allocated value string (caller must free) + * @param out_is_object Output: Whether the value is an object (vs string) + * @return true if found + */ +static bool extract_json_value(const char* json_obj, const char* key, char** out_value, + bool* out_is_object) { + if (!json_obj || !key || !out_value) { + return false; + } + + *out_value = nullptr; + *out_is_object = false; + + size_t len = strlen(json_obj); + bool in_string = false; + bool escaped = false; + + for (size_t i = 0; i < len; i++) { + char ch = json_obj[i]; + + if (escaped) { + escaped = false; + continue; + } + + if (ch == '\\') { + escaped = true; + continue; + } + + if (ch == '"') { + if (!in_string) { + // Start of a key string - extract it + size_t key_start = i + 1; + char* found_key = nullptr; + size_t key_end; + + if (extract_json_string(json_obj, key_start, len, &found_key, &key_end)) { + // Check if this key matches + bool matches = str_equals_ignore_case(found_key, key); + free(found_key); + + if (matches) { + // Skip to colon + size_t pos = skip_whitespace(json_obj, key_end, len); + if (pos < len && json_obj[pos] == ':') { + pos++; + pos = skip_whitespace(json_obj, pos, len); + + // Extract value + if (pos < len) { + if (json_obj[pos] == '"') { + // String value + size_t value_end; + if (extract_json_string(json_obj, pos + 1, len, out_value, + &value_end)) { + *out_is_object = false; + return true; + } + } else if (json_obj[pos] == '{') { + // Object value + size_t value_end; + if (extract_json_object_raw(json_obj, pos, len, out_value, + &value_end)) { + *out_is_object = true; + return true; + } + } + } + } + } + + // Move to end of key for continued scanning + i = key_end - 1; + } + } + in_string = !in_string; + } + } + + return false; +} + +/** + * @brief Get all keys from a JSON object (for fallback strategy) + */ +static std::vector get_json_keys(const char* json_obj) { + std::vector keys; + if (!json_obj) { + return keys; + } + + size_t len = strlen(json_obj); + bool in_string = false; + bool escaped = false; + int depth = 0; + + for (size_t i = 0; i < len; i++) { + char ch = json_obj[i]; + + if (escaped) { + escaped = false; + continue; + } + + if (ch == '\\') { + escaped = true; + continue; + } + + if (ch == '"') { + if (!in_string && depth == 1) { + // Start of a key at depth 1 (top-level) + size_t key_start = i + 1; + char* found_key = nullptr; + size_t key_end; + + if (extract_json_string(json_obj, key_start, len, &found_key, &key_end)) { + // Verify it's followed by colon + size_t pos = skip_whitespace(json_obj, key_end, len); + if (pos < len && json_obj[pos] == ':') { + keys.push_back(found_key); + } + free(found_key); + i = key_end - 1; + continue; + } + } + in_string = !in_string; + continue; + } + + if (!in_string) { + if (ch == '{') { + depth++; + } else if (ch == '}') { + depth--; + } + } + } + + return keys; +} + +/** + * @brief Check if key is a standard/reserved key + */ +static bool is_standard_key(const char* key) { + // Standard tool keys + for (int i = 0; TOOL_NAME_KEYS[i] != nullptr; i++) { + if (str_equals_ignore_case(key, TOOL_NAME_KEYS[i])) { + return true; + } + } + // Standard argument keys + for (int i = 0; ARGUMENT_KEYS[i] != nullptr; i++) { + if (str_equals_ignore_case(key, ARGUMENT_KEYS[i])) { + return true; + } + } + return false; +} + +/** + * @brief Escape a string for JSON output (manual implementation) + * + * Escapes special characters (quotes, backslashes, control characters) + * to produce valid JSON string content. + */ +static std::string escape_json_string(const char* str) { + if (!str) { + return ""; + } + + std::string result; + result.reserve(strlen(str) + 16); + + for (size_t i = 0; str[i]; i++) { + char c = str[i]; + switch (c) { + case '"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + default: + result += c; + break; + } + } + + return result; +} + +// ============================================================================= +// JSON NORMALIZATION +// ============================================================================= + +extern "C" rac_result_t rac_tool_call_normalize_json(const char* json_str, char** out_normalized) { + if (!json_str || !out_normalized) { + return RAC_ERROR_INVALID_ARGUMENT; + } + + size_t len = strlen(json_str); + std::string result; + result.reserve(len + 32); + + bool in_string = false; + + for (size_t i = 0; i < len; i++) { + char c = json_str[i]; + + // Track if we're inside a string + if (c == '"' && (i == 0 || json_str[i - 1] != '\\')) { + in_string = !in_string; + result += c; + continue; + } + + if (in_string) { + result += c; + continue; + } + + // Look for unquoted keys: { key: or , key: + if ((c == '{' || c == ',') && i + 1 < len) { + result += c; + + // Skip whitespace + size_t j = i + 1; + while (j < len && (json_str[j] == ' ' || json_str[j] == '\t' || json_str[j] == '\n')) { + result += json_str[j]; + j++; + } + + // Check if next is an unquoted identifier followed by colon + if (j < len && json_str[j] != '"' && json_str[j] != '{' && json_str[j] != '[') { + size_t key_start = j; + while (j < len && is_key_char(json_str[j])) { + j++; + } + + if (j < len && j > key_start) { + size_t key_end = j; + // Skip whitespace to find colon + while (j < len && (json_str[j] == ' ' || json_str[j] == '\t')) { + j++; + } + if (j < len && json_str[j] == ':') { + // This is an unquoted key - add quotes + result += '"'; + result.append(json_str + key_start, key_end - key_start); + result += '"'; + i = key_end - 1; // -1 because loop will increment + continue; + } + } + } + + i = j - 1; // -1 because loop will increment + continue; + } + + result += c; + } + + *out_normalized = static_cast(malloc(result.size() + 1)); + if (!*out_normalized) { + return RAC_ERROR_OUT_OF_MEMORY; + } + memcpy(*out_normalized, result.c_str(), result.size() + 1); + + return RAC_SUCCESS; +} + +// ============================================================================= +// TOOL NAME AND ARGUMENTS EXTRACTION +// ============================================================================= + +/** + * @brief Extract tool name and arguments using multiple strategies + * + * Strategies in order: + * 1. Standard format: {"tool": "name", "arguments": {...}} + * 2. Name/function variant: {"name": "name", "params": {...}} + * 3. Placeholder key with value being tool name + * 4. Tool name as key: {"calculate": "5 * 100"} + */ +static bool extract_tool_name_and_args(const char* json_obj, char** out_tool_name, + char** out_args_json) { + *out_tool_name = nullptr; + *out_args_json = nullptr; + + // Strategy 1 & 2: Try standard tool name keys + for (int i = 0; TOOL_NAME_KEYS[i] != nullptr; i++) { + char* value = nullptr; + bool is_obj = false; + if (extract_json_value(json_obj, TOOL_NAME_KEYS[i], &value, &is_obj)) { + if (!is_obj && value && strlen(value) > 0) { + *out_tool_name = value; + + // Now find arguments + for (int j = 0; ARGUMENT_KEYS[j] != nullptr; j++) { + char* args_value = nullptr; + bool args_is_obj = false; + if (extract_json_value(json_obj, ARGUMENT_KEYS[j], &args_value, &args_is_obj)) { + if (args_is_obj) { + *out_args_json = args_value; + } else { + // Wrap scalar in {"input": value} - escape the value for valid JSON + std::string escaped_args = escape_json_string(args_value); + size_t wrap_len = escaped_args.size() + 14; // {"input":"" } + null + *out_args_json = static_cast(malloc(wrap_len)); + if (*out_args_json) { + snprintf(*out_args_json, wrap_len, "{\"input\":\"%s\"}", escaped_args.c_str()); + } + free(args_value); + } + return true; + } + } + + // No arguments found - use empty object + *out_args_json = static_cast(malloc(3)); + if (*out_args_json) { + std::memcpy(*out_args_json, "{}", 3); + } + return true; + } + free(value); + } + } + + // Strategy 3 & 4: Tool name as key (non-standard key) + std::vector keys = get_json_keys(json_obj); + for (const auto& key : keys) { + if (!is_standard_key(key.c_str())) { + // Found a non-standard key - treat it as tool name + char* value = nullptr; + bool is_obj = false; + if (extract_json_value(json_obj, key.c_str(), &value, &is_obj)) { + *out_tool_name = static_cast(malloc(key.size() + 1)); + if (*out_tool_name) { + std::memcpy(*out_tool_name, key.c_str(), key.size() + 1); + } + + if (is_obj) { + // Value is object - use as arguments + *out_args_json = value; + } else if (value) { + // Value is scalar - wrap in {"input": value} - escape for valid JSON + std::string escaped_value = escape_json_string(value); + size_t wrap_len = escaped_value.size() + 14; // {"input":"" } + null + *out_args_json = static_cast(malloc(wrap_len)); + if (*out_args_json) { + snprintf(*out_args_json, wrap_len, "{\"input\":\"%s\"}", escaped_value.c_str()); + } + free(value); + } else { + *out_args_json = static_cast(malloc(3)); + if (*out_args_json) { + std::memcpy(*out_args_json, "{}", 3); + } + } + return true; + } + } + } + + return false; +} + +// ============================================================================= +// FORMAT-SPECIFIC PARSERS +// ============================================================================= + +/** + * @brief Parse LFM2 (Liquid AI) format: <|tool_call_start|>[func(arg="val")]<|tool_call_end|> + * + * LFM2 uses Pythonic function call syntax: + * [func_name(arg1="value1", arg2="value2")] + * + * @return true if successfully parsed, false otherwise + */ +static bool parse_lfm2_format(const char* llm_output, char** out_tool_name, char** out_args_json, + char** out_clean_text) { + *out_tool_name = nullptr; + *out_args_json = nullptr; + *out_clean_text = nullptr; + + RAC_LOG_INFO("ToolCalling", "parse_lfm2_format: input='%.200s'%s", + llm_output, strlen(llm_output) > 200 ? "..." : ""); + + // Find start tag + const char* start_tag = strstr(llm_output, TAG_LFM2_START); + if (!start_tag) { + RAC_LOG_INFO("ToolCalling", "LFM2 start tag '%s' not found in output", TAG_LFM2_START); + return false; + } + + RAC_LOG_INFO("ToolCalling", "Found LFM2 start tag at position: %zu", (size_t)(start_tag - llm_output)); + + size_t tag_start_pos = start_tag - llm_output; + const char* content_start = start_tag + strlen(TAG_LFM2_START); + + // Find end tag + const char* end_tag = strstr(content_start, TAG_LFM2_END); + if (!end_tag) { + // Try to parse until end of line or end of string + const char* line_end = strchr(content_start, '\n'); + if (line_end) { + end_tag = line_end; + } else { + end_tag = content_start + strlen(content_start); + } + } + + // Extract content between tags + size_t content_len = end_tag - content_start; + std::string content(content_start, content_len); + + // Parse Pythonic format: [func_name(arg1="val1", arg2="val2")] + // First, strip leading/trailing whitespace and brackets + size_t start = 0, end = content.size(); + while (start < end && (content[start] == ' ' || content[start] == '\n' || content[start] == '[')) { + start++; + } + while (end > start && (content[end - 1] == ' ' || content[end - 1] == '\n' || content[end - 1] == ']')) { + end--; + } + + if (start >= end) { + return false; + } + + std::string call_str = content.substr(start, end - start); + + RAC_LOG_INFO("ToolCalling", "LFM2 call_str: '%s'", call_str.c_str()); + + // Find function name (everything before '(') + size_t paren_pos = call_str.find('('); + if (paren_pos == std::string::npos) { + // No arguments - whole thing is function name + *out_tool_name = static_cast(malloc(call_str.size() + 1)); + if (*out_tool_name) { + std::memcpy(*out_tool_name, call_str.c_str(), call_str.size() + 1); + } + *out_args_json = static_cast(malloc(3)); + if (*out_args_json) { + std::memcpy(*out_args_json, "{}", 3); + } + } else { + std::string func_name = call_str.substr(0, paren_pos); + + // Trim whitespace from function name + while (!func_name.empty() && func_name.back() == ' ') { + func_name.pop_back(); + } + + *out_tool_name = static_cast(malloc(func_name.size() + 1)); + if (*out_tool_name) { + std::memcpy(*out_tool_name, func_name.c_str(), func_name.size() + 1); + } + + // Parse arguments: arg1="val1", arg2="val2", ... + // Convert to JSON format + size_t args_start = paren_pos + 1; + size_t args_end = call_str.rfind(')'); + if (args_end == std::string::npos) { + args_end = call_str.size(); + } + + std::string args_str = call_str.substr(args_start, args_end - args_start); + + RAC_LOG_INFO("ToolCalling", "LFM2 args_str: '%s' (paren=%zu, end=%zu)", + args_str.c_str(), paren_pos, args_end); + + // Convert Python-style args to JSON + std::string json_args = "{"; + bool first_arg = true; + bool in_string = false; + char string_char = 0; + std::string current_key; + std::string current_value; + bool parsing_key = true; + + for (size_t i = 0; i < args_str.size(); i++) { + char c = args_str[i]; + + if (in_string) { + if (c == string_char && (i == 0 || args_str[i - 1] != '\\')) { + in_string = false; + // End of value - escape key and value for valid JSON + if (!current_key.empty()) { + if (!first_arg) { + json_args += ","; + } + std::string escaped_key = escape_json_string(current_key.c_str()); + std::string escaped_val = escape_json_string(current_value.c_str()); + json_args += "\"" + escaped_key + "\":\"" + escaped_val + "\""; + first_arg = false; + current_key.clear(); + current_value.clear(); + parsing_key = true; + } + } else { + current_value += c; + } + } else { + if (c == '"' || c == '\'') { + in_string = true; + string_char = c; + parsing_key = false; + } else if (c == '=') { + parsing_key = false; + } else if (c == ',') { + // Handle unquoted values or numeric values + if (!current_key.empty() && !current_value.empty()) { + if (!first_arg) { + json_args += ","; + } + // Check if value is numeric (handles edge cases) + bool is_numeric = !current_value.empty(); + bool has_dot = false; + bool has_minus = false; + for (size_t i = 0; i < current_value.size() && is_numeric; i++) { + char vc = current_value[i]; + if (vc == '-') { + if (i != 0 || has_minus) is_numeric = false; + has_minus = true; + } else if (vc == '.') { + if (has_dot) is_numeric = false; + has_dot = true; + } else if (!isdigit(vc)) { + is_numeric = false; + } + } + if (current_value == "-" || current_value == ".") is_numeric = false; + // Escape key always; escape value only for non-numeric strings + std::string escaped_key = escape_json_string(current_key.c_str()); + if (is_numeric) { + json_args += "\"" + escaped_key + "\":" + current_value; + } else { + std::string escaped_val = escape_json_string(current_value.c_str()); + json_args += "\"" + escaped_key + "\":\"" + escaped_val + "\""; + } + first_arg = false; + } + current_key.clear(); + current_value.clear(); + parsing_key = true; + } else if (c != ' ' || in_string) { + if (parsing_key) { + current_key += c; + } else { + current_value += c; + } + } + } + } + + // Handle last argument + if (!current_key.empty() && !current_value.empty()) { + if (!first_arg) { + json_args += ","; + } + // Check if value is numeric (handles edge cases) + bool is_numeric = !current_value.empty(); + bool has_dot = false; + bool has_minus = false; + for (size_t i = 0; i < current_value.size() && is_numeric; i++) { + char vc = current_value[i]; + if (vc == '-') { + if (i != 0 || has_minus) is_numeric = false; + has_minus = true; + } else if (vc == '.') { + if (has_dot) is_numeric = false; + has_dot = true; + } else if (!isdigit(vc)) { + is_numeric = false; + } + } + if (current_value == "-" || current_value == ".") is_numeric = false; + // Escape key always; escape value only for non-numeric strings + std::string escaped_key = escape_json_string(current_key.c_str()); + if (is_numeric) { + json_args += "\"" + escaped_key + "\":" + current_value; + } else { + std::string escaped_val = escape_json_string(current_value.c_str()); + json_args += "\"" + escaped_key + "\":\"" + escaped_val + "\""; + } + } + + json_args += "}"; + + RAC_LOG_INFO("ToolCalling", "LFM2 parsed json_args: '%s'", json_args.c_str()); + + *out_args_json = static_cast(malloc(json_args.size() + 1)); + if (*out_args_json) { + std::memcpy(*out_args_json, json_args.c_str(), json_args.size() + 1); + } + } + + RAC_LOG_INFO("ToolCalling", "LFM2 RESULT: tool='%s', args='%s'", + *out_tool_name ? *out_tool_name : "(null)", + *out_args_json ? *out_args_json : "(null)"); + + // Build clean text + std::string clean_text; + clean_text.append(llm_output, tag_start_pos); + + const char* after_end = end_tag; + if (strstr(end_tag, TAG_LFM2_END) == end_tag) { + after_end = end_tag + strlen(TAG_LFM2_END); + } + if (*after_end) { + clean_text.append(after_end); + } + + // Trim + size_t trim_start = 0, trim_end = clean_text.size(); + while (trim_start < trim_end && (clean_text[trim_start] == ' ' || clean_text[trim_start] == '\n')) { + trim_start++; + } + while (trim_end > trim_start && (clean_text[trim_end - 1] == ' ' || clean_text[trim_end - 1] == '\n')) { + trim_end--; + } + + *out_clean_text = static_cast(malloc(trim_end - trim_start + 1)); + if (*out_clean_text) { + memcpy(*out_clean_text, clean_text.c_str() + trim_start, trim_end - trim_start); + (*out_clean_text)[trim_end - trim_start] = '\0'; + } + + return *out_tool_name != nullptr; +} + +/** + * @brief Parse default format: JSON + * + * This is the original SDK format with JSON inside the tags. + * Handles edge cases like missing closing tags, unquoted keys, etc. + * + * @return true if successfully parsed, false otherwise + */ +static bool parse_default_format(const char* llm_output, char** out_tool_name, char** out_args_json, + char** out_clean_text); + +// ============================================================================= +// PARSE TOOL CALL - Main entry points +// ============================================================================= + +extern "C" rac_result_t rac_tool_call_parse(const char* llm_output, rac_tool_call_t* out_result) { + // Auto-detect format from output, then parse + rac_tool_call_format_t detected = rac_tool_call_detect_format(llm_output); + return rac_tool_call_parse_with_format(llm_output, detected, out_result); +} + +/** + * @brief Implementation of parse_default_format + * + * Parses the default JSON format. + */ +static bool parse_default_format(const char* llm_output, char** out_tool_name, char** out_args_json, + char** out_clean_text) { + *out_tool_name = nullptr; + *out_args_json = nullptr; + *out_clean_text = nullptr; + + size_t output_len = strlen(llm_output); + + // Find tag + const char* tag_start = find_str(llm_output, TAG_DEFAULT_START); + if (!tag_start) { + return false; + } + + size_t tag_start_pos = tag_start - llm_output; + size_t json_start_pos = tag_start_pos + strlen(TAG_DEFAULT_START); + + // Find end tag + const char* tag_end = find_str(llm_output + json_start_pos, TAG_DEFAULT_END); + size_t json_end_pos; + bool has_closing_tag; + + if (tag_end) { + json_end_pos = (tag_end - llm_output); + has_closing_tag = true; + } else { + // No closing tag - find JSON by matching braces + size_t brace_end; + if (!find_matching_brace(llm_output, json_start_pos, &brace_end)) { + return false; + } + json_end_pos = brace_end + 1; + has_closing_tag = false; + } + + // Extract JSON between tags + size_t json_len = json_end_pos - json_start_pos; + char* tool_json_str = static_cast(malloc(json_len + 1)); + if (!tool_json_str) { + return false; + } + memcpy(tool_json_str, llm_output + json_start_pos, json_len); + tool_json_str[json_len] = '\0'; + + // Normalize JSON (handle unquoted keys) + char* normalized_json = nullptr; + rac_result_t norm_result = rac_tool_call_normalize_json(tool_json_str, &normalized_json); + free(tool_json_str); + + if (norm_result != RAC_SUCCESS || !normalized_json) { + return false; + } + + // Extract tool name and arguments + if (!extract_tool_name_and_args(normalized_json, out_tool_name, out_args_json)) { + free(normalized_json); + return false; + } + + free(normalized_json); + + // Build clean text (everything except the tool call tags) + std::string clean_text; + clean_text.append(llm_output, tag_start_pos); + + if (has_closing_tag) { + size_t after_tag = json_end_pos + strlen(TAG_DEFAULT_END); + if (after_tag < output_len) { + clean_text.append(llm_output + after_tag); + } + } else { + if (json_end_pos < output_len) { + clean_text.append(llm_output + json_end_pos); + } + } + + // Trim whitespace + size_t trim_start, trim_end; + trim_whitespace(clean_text.c_str(), clean_text.size(), &trim_start, &trim_end); + + size_t clean_len = trim_end - trim_start; + *out_clean_text = static_cast(malloc(clean_len + 1)); + if (*out_clean_text) { + memcpy(*out_clean_text, clean_text.c_str() + trim_start, clean_len); + (*out_clean_text)[clean_len] = '\0'; + } + + return *out_tool_name != nullptr; +} + +extern "C" rac_result_t rac_tool_call_parse_with_format(const char* llm_output, + rac_tool_call_format_t format, + rac_tool_call_t* out_result) { + if (!llm_output || !out_result) { + return RAC_ERROR_INVALID_ARGUMENT; + } + + // Initialize result + out_result->has_tool_call = RAC_FALSE; + out_result->tool_name = nullptr; + out_result->arguments_json = nullptr; + out_result->clean_text = nullptr; + out_result->call_id = 0; + out_result->format = RAC_TOOL_FORMAT_DEFAULT; + + size_t output_len = strlen(llm_output); + + // Parse using the appropriate format parser + char* tool_name = nullptr; + char* args_json = nullptr; + char* clean_text = nullptr; + bool parsed = false; + + switch (format) { + case RAC_TOOL_FORMAT_DEFAULT: + parsed = parse_default_format(llm_output, &tool_name, &args_json, &clean_text); + break; + + case RAC_TOOL_FORMAT_LFM2: + parsed = parse_lfm2_format(llm_output, &tool_name, &args_json, &clean_text); + break; + + default: + parsed = false; + break; + } + + if (parsed && tool_name) { + out_result->has_tool_call = RAC_TRUE; + out_result->tool_name = tool_name; + out_result->arguments_json = args_json; + out_result->clean_text = clean_text; + out_result->format = format; + out_result->call_id = static_cast(time(nullptr)) * 1000 + (rand() % 1000); + } else { + // Parsing failed - clean up any partial results + if (tool_name) free(tool_name); + if (args_json) free(args_json); + if (clean_text) free(clean_text); + + // Return original text as clean_text + out_result->clean_text = static_cast(malloc(output_len + 1)); + if (out_result->clean_text) { + std::memcpy(out_result->clean_text, llm_output, output_len + 1); + } + } + + return RAC_SUCCESS; +} + +extern "C" void rac_tool_call_free(rac_tool_call_t* result) { + if (!result) { + return; + } + + if (result->tool_name) { + free(result->tool_name); + result->tool_name = nullptr; + } + + if (result->arguments_json) { + free(result->arguments_json); + result->arguments_json = nullptr; + } + + if (result->clean_text) { + free(result->clean_text); + result->clean_text = nullptr; + } + + result->has_tool_call = RAC_FALSE; + result->call_id = 0; +} + +// ============================================================================= +// PROMPT FORMATTING +// ============================================================================= + +/** + * @brief Get parameter type name + */ +static const char* get_param_type_name(rac_tool_param_type_t type) { + switch (type) { + case RAC_TOOL_PARAM_STRING: + return "string"; + case RAC_TOOL_PARAM_NUMBER: + return "number"; + case RAC_TOOL_PARAM_BOOLEAN: + return "boolean"; + case RAC_TOOL_PARAM_OBJECT: + return "object"; + case RAC_TOOL_PARAM_ARRAY: + return "array"; + default: + return "unknown"; + } +} + +/** + * @brief Generate format-specific tool calling instructions + * + * Returns the format-specific syntax, examples, and rules. + */ +static std::string get_format_instructions(rac_tool_call_format_t format) { + std::string instructions; + + switch (format) { + case RAC_TOOL_FORMAT_LFM2: + // Liquid AI LFM2 format + instructions += "TOOL CALLING FORMAT (LFM2):\n"; + instructions += "When you need to use a tool, output ONLY this format:\n"; + instructions += "<|tool_call_start|>[TOOL_NAME(param=\"VALUE_FROM_USER_QUERY\")]<|tool_call_end|>\n\n"; + + instructions += "CRITICAL: Extract the EXACT value from the user's question:\n"; + instructions += "- User asks 'weather in Tokyo' -> <|tool_call_start|>[get_weather(location=\"Tokyo\")]<|tool_call_end|>\n"; + instructions += "- User asks 'weather in sf' -> <|tool_call_start|>[get_weather(location=\"San Francisco\")]<|tool_call_end|>\n\n"; + + instructions += "RULES:\n"; + instructions += "1. For greetings or general chat, respond normally without tools\n"; + instructions += "2. Use Python-style function call syntax inside the tags\n"; + instructions += "3. String values MUST be quoted with double quotes\n"; + instructions += "4. Multiple arguments are separated by commas"; + break; + + case RAC_TOOL_FORMAT_DEFAULT: + default: + // Default SDK format + instructions += "TOOL CALLING FORMAT - YOU MUST USE THIS EXACT FORMAT:\n"; + instructions += "When you need to use a tool, output ONLY this (no other text before or after):\n"; + instructions += "{\"tool\": \"TOOL_NAME\", \"arguments\": {\"PARAM_NAME\": \"VALUE_FROM_USER_QUERY\"}}\n\n"; + + instructions += "CRITICAL: Extract the EXACT value from the user's question:\n"; + instructions += "- User asks 'weather in Tokyo' -> {\"tool\": \"get_weather\", \"arguments\": {\"location\": \"Tokyo\"}}\n"; + instructions += "- User asks 'weather in sf' -> {\"tool\": \"get_weather\", \"arguments\": {\"location\": \"San Francisco\"}}\n\n"; + + instructions += "RULES:\n"; + instructions += "1. For greetings or general chat, respond normally without tools\n"; + instructions += "2. When using a tool, output ONLY the tag, nothing else\n"; + instructions += "3. Use the exact parameter names shown in the tool definitions above"; + break; + } + + return instructions; +} + +/** + * @brief Generate format-specific example for JSON prompt + */ +static std::string get_format_example_json(rac_tool_call_format_t format) { + std::string example; + + switch (format) { + case RAC_TOOL_FORMAT_LFM2: + // LFM2 format - enhanced with more math examples for better reliability + example += "## OUTPUT FORMAT\n"; + example += "You MUST respond with ONLY a tool call in this exact format:\n"; + example += "<|tool_call_start|>[function_name(param=\"value\")]<|tool_call_end|>\n\n"; + example += "CRITICAL: Always include the FULL format with <|tool_call_start|> and <|tool_call_end|> tags.\n\n"; + example += "## EXAMPLES\n"; + example += "Q: What's the weather in NYC?\n"; + example += "A: <|tool_call_start|>[get_weather(location=\"New York\")]<|tool_call_end|>\n\n"; + example += "Q: weather in sf\n"; + example += "A: <|tool_call_start|>[get_weather(location=\"San Francisco\")]<|tool_call_end|>\n\n"; + example += "Q: calculate 2+2\n"; + example += "A: <|tool_call_start|>[calculate(expression=\"2+2\")]<|tool_call_end|>\n\n"; + example += "Q: What's 5*10?\n"; + example += "A: <|tool_call_start|>[calculate(expression=\"5*10\")]<|tool_call_end|>\n\n"; + example += "Q: What is 100/4?\n"; + example += "A: <|tool_call_start|>[calculate(expression=\"100/4\")]<|tool_call_end|>\n"; + break; + + case RAC_TOOL_FORMAT_DEFAULT: + default: + example += "## OUTPUT FORMAT\n"; + example += "You MUST respond with ONLY a tool call in this exact format:\n"; + example += "{\"tool\": \"function_name\", \"arguments\": {\"param\": \"value\"}}\n\n"; + example += "## EXAMPLES\n"; + example += "Q: What's the weather in NYC?\n"; + example += "A: {\"tool\": \"get_weather\", \"arguments\": {\"location\": \"New York\"}}\n\n"; + example += "Q: weather in sf\n"; + example += "A: {\"tool\": \"get_weather\", \"arguments\": {\"location\": \"San Francisco\"}}\n\n"; + example += "Q: calculate 2+2\n"; + example += "A: {\"tool\": \"calculate\", \"arguments\": {\"expression\": \"2+2\"}}\n"; + break; + } + + return example; +} + +// ============================================================================= +// FORMAT-AWARE PROMPT GENERATION +// ============================================================================= + +extern "C" rac_result_t rac_tool_call_format_prompt_with_format(const rac_tool_definition_t* definitions, + size_t num_definitions, + rac_tool_call_format_t format, + char** out_prompt) { + if (!out_prompt) { + return RAC_ERROR_INVALID_ARGUMENT; + } + + if (!definitions || num_definitions == 0) { + *out_prompt = static_cast(malloc(1)); + if (*out_prompt) { + (*out_prompt)[0] = '\0'; + } + return RAC_SUCCESS; + } + + rac_tool_call_format_t actual_format = format; + + std::string prompt; + prompt.reserve(1024); + + prompt += "You have access to these tools:\n\n"; + + for (size_t i = 0; i < num_definitions; i++) { + const rac_tool_definition_t& tool = definitions[i]; + + prompt += "- "; + prompt += tool.name ? tool.name : "unknown"; + prompt += ": "; + prompt += tool.description ? tool.description : ""; + prompt += "\n"; + + if (tool.parameters && tool.num_parameters > 0) { + prompt += " Parameters:\n"; + for (size_t j = 0; j < tool.num_parameters; j++) { + const rac_tool_parameter_t& param = tool.parameters[j]; + prompt += " - "; + prompt += param.name ? param.name : "unknown"; + prompt += " ("; + prompt += get_param_type_name(param.type); + if (param.required) { + prompt += ", required"; + } + prompt += "): "; + prompt += param.description ? param.description : ""; + prompt += "\n"; + } + } + prompt += "\n"; + } + + // Add format-specific instructions + prompt += get_format_instructions(actual_format); + + *out_prompt = static_cast(malloc(prompt.size() + 1)); + if (!*out_prompt) { + return RAC_ERROR_OUT_OF_MEMORY; + } + memcpy(*out_prompt, prompt.c_str(), prompt.size() + 1); + + return RAC_SUCCESS; +} + +extern "C" rac_result_t rac_tool_call_format_prompt_json_with_format(const char* tools_json, + rac_tool_call_format_t format, + char** out_prompt) { + if (!out_prompt) { + return RAC_ERROR_INVALID_ARGUMENT; + } + + if (!tools_json || strlen(tools_json) == 0 || strcmp(tools_json, "[]") == 0) { + *out_prompt = static_cast(malloc(1)); + if (*out_prompt) { + (*out_prompt)[0] = '\0'; + } + return RAC_SUCCESS; + } + + rac_tool_call_format_t actual_format = format; + + std::string prompt; + prompt.reserve(1024 + strlen(tools_json)); + + prompt += "# TOOLS\n"; + prompt += tools_json; + prompt += "\n\n"; + + // Add format-specific example with direct instructions + prompt += get_format_example_json(actual_format); + + prompt += "\n\n## RULES\n"; + prompt += "- Weather question = call get_weather\n"; + prompt += "- Math/calculation question (add, subtract, multiply, divide, \"what's X*Y\", etc.) = call calculate with the EXPRESSION as a string\n"; + prompt += "- Time question = call get_current_time\n"; + prompt += "- DO NOT compute answers yourself. ALWAYS use the tool with the original expression.\n"; + + // Format-specific tag instructions + if (actual_format == RAC_TOOL_FORMAT_LFM2) { + prompt += "- ALWAYS include <|tool_call_start|> and <|tool_call_end|> tags.\n"; + } else { + prompt += "- ALWAYS include and tags.\n"; + } + + RAC_LOG_INFO("ToolCalling", "Generated tool prompt (format=%d): %.500s...", + (int)actual_format, prompt.c_str()); + + *out_prompt = static_cast(malloc(prompt.size() + 1)); + if (!*out_prompt) { + return RAC_ERROR_OUT_OF_MEMORY; + } + memcpy(*out_prompt, prompt.c_str(), prompt.size() + 1); + + return RAC_SUCCESS; +} + +// ============================================================================= +// LEGACY PROMPT GENERATION (uses DEFAULT format) +// ============================================================================= + +extern "C" rac_result_t rac_tool_call_format_prompt(const rac_tool_definition_t* definitions, + size_t num_definitions, char** out_prompt) { + // Delegate to format-aware version with DEFAULT format + return rac_tool_call_format_prompt_with_format(definitions, num_definitions, + RAC_TOOL_FORMAT_DEFAULT, out_prompt); +} + +extern "C" rac_result_t rac_tool_call_format_prompt_json(const char* tools_json, char** out_prompt) { + // Delegate to format-aware version with DEFAULT format + return rac_tool_call_format_prompt_json_with_format(tools_json, RAC_TOOL_FORMAT_DEFAULT, out_prompt); +} + +extern "C" rac_result_t rac_tool_call_format_prompt_json_with_format_name(const char* tools_json, + const char* format_name, + char** out_prompt) { + // Convert format name to enum and delegate + rac_tool_call_format_t format = rac_tool_call_format_from_name(format_name); + RAC_LOG_INFO("ToolCalling", "Formatting prompt with format_name='%s' -> enum=%d", + format_name ? format_name : "null", (int)format); + return rac_tool_call_format_prompt_json_with_format(tools_json, format, out_prompt); +} + +extern "C" rac_result_t rac_tool_call_build_initial_prompt(const char* user_prompt, + const char* tools_json, + const rac_tool_calling_options_t* options, + char** out_prompt) { + if (!user_prompt || !out_prompt) { + return RAC_ERROR_INVALID_ARGUMENT; + } + + // Get format from options (default to DEFAULT) + rac_tool_call_format_t format = options ? options->format : RAC_TOOL_FORMAT_DEFAULT; + + // Format tools prompt with the specified format + char* tools_prompt = nullptr; + rac_result_t result = rac_tool_call_format_prompt_json_with_format(tools_json, format, &tools_prompt); + if (result != RAC_SUCCESS) { + return result; + } + + std::string full_prompt; + full_prompt.reserve(2048); + + // Add system prompt if provided + if (options && options->system_prompt) { + if (options->replace_system_prompt) { + // Replace entirely - just use the system prompt + full_prompt += options->system_prompt; + full_prompt += "\n\n"; + } else { + // Append tool instructions after system prompt + full_prompt += options->system_prompt; + full_prompt += "\n\n"; + } + } + + // Add tools prompt (unless replace_system_prompt is true and we already have system_prompt) + if (!(options && options->replace_system_prompt && options->system_prompt)) { + if (tools_prompt && strlen(tools_prompt) > 0) { + full_prompt += tools_prompt; + full_prompt += "\n\n"; + } + } + + // Add user prompt + full_prompt += "User: "; + full_prompt += user_prompt; + + free(tools_prompt); + + *out_prompt = static_cast(malloc(full_prompt.size() + 1)); + if (!*out_prompt) { + return RAC_ERROR_OUT_OF_MEMORY; + } + memcpy(*out_prompt, full_prompt.c_str(), full_prompt.size() + 1); + + return RAC_SUCCESS; +} + +extern "C" rac_result_t rac_tool_call_build_followup_prompt(const char* original_user_prompt, + const char* tools_prompt, + const char* tool_name, + const char* tool_result_json, + rac_bool_t keep_tools_available, + char** out_prompt) { + if (!original_user_prompt || !tool_name || !out_prompt) { + return RAC_ERROR_INVALID_ARGUMENT; + } + + std::string prompt; + prompt.reserve(1024); + + // Include tools again if keepToolsAvailable + if (keep_tools_available && tools_prompt && strlen(tools_prompt) > 0) { + prompt += tools_prompt; + prompt += "\n\n"; + } + + prompt += "Previous user question: "; + prompt += original_user_prompt; + prompt += "\n\n"; + + prompt += "Tool '"; + prompt += tool_name; + prompt += "' was executed with this result:\n"; + prompt += tool_result_json ? tool_result_json : "{}"; + prompt += "\n\n"; + + if (keep_tools_available) { + prompt += "Using this information, respond to the user's original question. "; + prompt += "You may use additional tools if needed."; + } else { + prompt += "Using this information, provide a natural response to the user's original question. "; + prompt += "Do not use any tool tags in your response - just respond naturally."; + } + + *out_prompt = static_cast(malloc(prompt.size() + 1)); + if (!*out_prompt) { + return RAC_ERROR_OUT_OF_MEMORY; + } + memcpy(*out_prompt, prompt.c_str(), prompt.size() + 1); + + return RAC_SUCCESS; +} + +// ============================================================================= +// JSON SERIALIZATION UTILITIES +// ============================================================================= + +extern "C" rac_result_t rac_tool_call_definitions_to_json(const rac_tool_definition_t* definitions, + size_t num_definitions, + char** out_json) { + if (!out_json) { + return RAC_ERROR_INVALID_ARGUMENT; + } + + if (!definitions || num_definitions == 0) { + *out_json = static_cast(malloc(3)); + if (*out_json) { + std::memcpy(*out_json, "[]", 3); + } + return RAC_SUCCESS; + } + + std::string json; + json.reserve(512 * num_definitions); + json += "["; + + for (size_t i = 0; i < num_definitions; i++) { + if (i > 0) { + json += ","; + } + + const rac_tool_definition_t& tool = definitions[i]; + + json += "{"; + json += "\"name\":\""; + json += escape_json_string(tool.name); + json += "\","; + json += "\"description\":\""; + json += escape_json_string(tool.description); + json += "\","; + json += "\"parameters\":["; + + if (tool.parameters) { + for (size_t j = 0; j < tool.num_parameters; j++) { + if (j > 0) { + json += ","; + } + + const rac_tool_parameter_t& param = tool.parameters[j]; + + json += "{"; + json += "\"name\":\""; + json += escape_json_string(param.name); + json += "\","; + json += "\"type\":\""; + json += get_param_type_name(param.type); + json += "\","; + json += "\"description\":\""; + json += escape_json_string(param.description); + json += "\","; + json += "\"required\":"; + json += param.required ? "true" : "false"; + json += "}"; + } + } + + json += "]"; + + if (tool.category) { + json += ",\"category\":\""; + json += escape_json_string(tool.category); + json += "\""; + } + + json += "}"; + } + + json += "]"; + + *out_json = static_cast(malloc(json.size() + 1)); + if (!*out_json) { + return RAC_ERROR_OUT_OF_MEMORY; + } + memcpy(*out_json, json.c_str(), json.size() + 1); + + return RAC_SUCCESS; +} + +extern "C" rac_result_t rac_tool_call_result_to_json(const char* tool_name, rac_bool_t success, + const char* result_json, + const char* error_message, + char** out_json) { + if (!tool_name || !out_json) { + return RAC_ERROR_INVALID_ARGUMENT; + } + + std::string json; + json.reserve(256); + + json += "{"; + json += "\"toolName\":\""; + json += escape_json_string(tool_name); + json += "\","; + json += "\"success\":"; + json += success ? "true" : "false"; + + if (success && result_json) { + json += ",\"result\":"; + json += result_json; // Already JSON + } + + if (!success && error_message) { + json += ",\"error\":\""; + json += escape_json_string(error_message); + json += "\""; + } + + json += "}"; + + *out_json = static_cast(malloc(json.size() + 1)); + if (!*out_json) { + return RAC_ERROR_OUT_OF_MEMORY; + } + memcpy(*out_json, json.c_str(), json.size() + 1); + + return RAC_SUCCESS; +} diff --git a/sdk/runanywhere-commons/src/jni/runanywhere_commons_jni.cpp b/sdk/runanywhere-commons/src/jni/runanywhere_commons_jni.cpp index da2e337a4..1156b1630 100644 --- a/sdk/runanywhere-commons/src/jni/runanywhere_commons_jni.cpp +++ b/sdk/runanywhere-commons/src/jni/runanywhere_commons_jni.cpp @@ -40,6 +40,7 @@ #include "rac/infrastructure/network/rac_environment.h" #include "rac/infrastructure/telemetry/rac_telemetry_manager.h" #include "rac/infrastructure/telemetry/rac_telemetry_types.h" +#include "rac/features/llm/rac_tool_calling.h" // NOTE: Backend headers are NOT included here. // Backend registration is handled by their respective JNI libraries: @@ -3260,6 +3261,198 @@ JNIEXPORT jint JNICALL Java_com_runanywhere_sdk_native_bridge_RunAnywhereBridge_ return static_cast(result); } +// ============================================================================= +// TOOL CALLING API (rac_tool_calling.h) +// Mirrors Swift SDK's CppBridge+ToolCalling.swift +// ============================================================================= + +JNIEXPORT jstring JNICALL +Java_com_runanywhere_sdk_native_bridge_RunAnywhereBridge_racToolCallParse(JNIEnv* env, jclass clazz, + jstring llmOutput) { + std::string outputStr = getCString(env, llmOutput); + rac_tool_call_t result; + + rac_result_t rc = rac_tool_call_parse(outputStr.c_str(), &result); + + // Build JSON response + std::string json = "{"; + json += "\"hasToolCall\":"; + json += (result.has_tool_call == RAC_TRUE) ? "true" : "false"; + json += ",\"cleanText\":\""; + + // Escape clean text + if (result.clean_text) { + for (const char* p = result.clean_text; *p; p++) { + switch (*p) { + case '"': json += "\\\""; break; + case '\\': json += "\\\\"; break; + case '\n': json += "\\n"; break; + case '\r': json += "\\r"; break; + case '\t': json += "\\t"; break; + default: json += *p; break; + } + } + } + json += "\""; + + if (result.has_tool_call == RAC_TRUE) { + json += ",\"toolName\":\""; + if (result.tool_name) json += result.tool_name; + json += "\",\"argumentsJson\":"; + if (result.arguments_json) { + // Validate that arguments_json is valid JSON object/array before inserting + // This prevents malformed JSON from breaking the response + std::string args(result.arguments_json); + // Trim leading whitespace + size_t start = args.find_first_not_of(" \t\n\r"); + if (start != std::string::npos && (args[start] == '{' || args[start] == '[')) { + // Appears to be valid JSON object/array - insert directly + json += args; + } else { + // Fallback: not a valid JSON object/array, use empty object + LOGe("racToolCallParse: arguments_json is not valid JSON object/array, using empty object"); + json += "{}"; + } + } else { + json += "{}"; + } + json += ",\"callId\":"; + json += std::to_string(result.call_id); + } + + json += "}"; + + rac_tool_call_free(&result); + return env->NewStringUTF(json.c_str()); +} + +JNIEXPORT jstring JNICALL +Java_com_runanywhere_sdk_native_bridge_RunAnywhereBridge_racToolCallFormatPromptJson( + JNIEnv* env, jclass clazz, jstring toolsJson) { + std::string toolsStr = getCString(env, toolsJson); + char* prompt = nullptr; + + rac_result_t rc = rac_tool_call_format_prompt_json(toolsStr.c_str(), &prompt); + + if (rc != RAC_SUCCESS || prompt == nullptr) { + return nullptr; + } + + jstring result = env->NewStringUTF(prompt); + rac_free(prompt); + return result; +} + +JNIEXPORT jstring JNICALL +Java_com_runanywhere_sdk_native_bridge_RunAnywhereBridge_racToolCallFormatPromptJsonWithFormat( + JNIEnv* env, jclass clazz, jstring toolsJson, jint format) { + std::string toolsStr = getCString(env, toolsJson); + char* prompt = nullptr; + + rac_result_t rc = rac_tool_call_format_prompt_json_with_format( + toolsStr.c_str(), + static_cast(format), + &prompt + ); + + if (rc != RAC_SUCCESS || prompt == nullptr) { + return nullptr; + } + + jstring result = env->NewStringUTF(prompt); + rac_free(prompt); + return result; +} + +JNIEXPORT jstring JNICALL +Java_com_runanywhere_sdk_native_bridge_RunAnywhereBridge_racToolCallFormatPromptJsonWithFormatName( + JNIEnv* env, jclass clazz, jstring toolsJson, jstring formatName) { + std::string toolsStr = getCString(env, toolsJson); + std::string formatStr = getCString(env, formatName); + char* prompt = nullptr; + + // Use string-based API (C++ is single source of truth for format names) + rac_result_t rc = rac_tool_call_format_prompt_json_with_format_name( + toolsStr.c_str(), + formatStr.c_str(), + &prompt + ); + + if (rc != RAC_SUCCESS || prompt == nullptr) { + return nullptr; + } + + jstring result = env->NewStringUTF(prompt); + rac_free(prompt); + return result; +} + +JNIEXPORT jstring JNICALL +Java_com_runanywhere_sdk_native_bridge_RunAnywhereBridge_racToolCallBuildInitialPrompt( + JNIEnv* env, jclass clazz, jstring userPrompt, jstring toolsJson, jstring optionsJson) { + std::string userStr = getCString(env, userPrompt); + std::string toolsStr = getCString(env, toolsJson); + + // Parse options if provided (simplified - use defaults for now) + rac_tool_calling_options_t options = {5, RAC_TRUE, 0.7f, 1024, nullptr, RAC_FALSE, RAC_FALSE}; + + char* prompt = nullptr; + rac_result_t rc = rac_tool_call_build_initial_prompt(userStr.c_str(), toolsStr.c_str(), &options, &prompt); + + if (rc != RAC_SUCCESS || prompt == nullptr) { + return nullptr; + } + + jstring result = env->NewStringUTF(prompt); + rac_free(prompt); + return result; +} + +JNIEXPORT jstring JNICALL +Java_com_runanywhere_sdk_native_bridge_RunAnywhereBridge_racToolCallBuildFollowupPrompt( + JNIEnv* env, jclass clazz, jstring originalPrompt, jstring toolsPrompt, jstring toolName, + jstring toolResultJson, jboolean keepToolsAvailable) { + std::string originalStr = getCString(env, originalPrompt); + std::string toolsPromptStr = getCString(env, toolsPrompt); + std::string toolNameStr = getCString(env, toolName); + std::string resultJsonStr = getCString(env, toolResultJson); + + char* prompt = nullptr; + rac_result_t rc = rac_tool_call_build_followup_prompt( + originalStr.c_str(), + toolsPromptStr.empty() ? nullptr : toolsPromptStr.c_str(), + toolNameStr.c_str(), + resultJsonStr.c_str(), + keepToolsAvailable ? RAC_TRUE : RAC_FALSE, + &prompt); + + if (rc != RAC_SUCCESS || prompt == nullptr) { + return nullptr; + } + + jstring result = env->NewStringUTF(prompt); + rac_free(prompt); + return result; +} + +JNIEXPORT jstring JNICALL +Java_com_runanywhere_sdk_native_bridge_RunAnywhereBridge_racToolCallNormalizeJson(JNIEnv* env, + jclass clazz, + jstring jsonStr) { + std::string inputStr = getCString(env, jsonStr); + char* normalized = nullptr; + + rac_result_t rc = rac_tool_call_normalize_json(inputStr.c_str(), &normalized); + + if (rc != RAC_SUCCESS || normalized == nullptr) { + return nullptr; + } + + jstring result = env->NewStringUTF(normalized); + rac_free(normalized); + return result; +} + } // extern "C" // ============================================================================= diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge.dart index c831a7462..52bf1d761 100644 --- a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge.dart +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge.dart @@ -24,6 +24,7 @@ import 'package:runanywhere/native/dart_bridge_state.dart'; import 'package:runanywhere/native/dart_bridge_storage.dart'; import 'package:runanywhere/native/dart_bridge_stt.dart'; import 'package:runanywhere/native/dart_bridge_telemetry.dart'; +import 'package:runanywhere/native/dart_bridge_tool_calling.dart'; import 'package:runanywhere/native/dart_bridge_tts.dart'; import 'package:runanywhere/native/dart_bridge_vad.dart'; import 'package:runanywhere/native/dart_bridge_voice_agent.dart'; diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tool_calling.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tool_calling.dart new file mode 100644 index 000000000..44c49faf4 --- /dev/null +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tool_calling.dart @@ -0,0 +1,437 @@ +/// DartBridge+ToolCalling +/// +/// Tool calling bridge - wraps C++ tool calling functions. +/// +/// *** SINGLE SOURCE OF TRUTH FOR TOOL CALLING LOGIC IS IN COMMONS C++ *** +/// +/// This is a THIN WRAPPER around rac_tool_calling.h functions. +/// NO LOCAL PARSING LOGIC - everything calls through to C++. +/// +/// Platform SDKs handle ONLY: +/// - Tool registry (Dart closures) +/// - Tool execution (Dart async calls) +library dart_bridge_tool_calling; + +import 'dart:convert'; +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; + +import 'package:runanywhere/foundation/logging/sdk_logger.dart'; +import 'package:runanywhere/native/ffi_types.dart'; +import 'package:runanywhere/native/platform_loader.dart'; + +/// Tool call parse result from C++ +class ToolCallParseResult { + final bool hasToolCall; + final String cleanText; + final String? toolName; + final Map? arguments; + final int callId; + + ToolCallParseResult({ + required this.hasToolCall, + required this.cleanText, + this.toolName, + this.arguments, + required this.callId, + }); +} + +/// Tool calling bridge for C++ interop. +/// +/// *** ALL PARSING LOGIC IS IN C++ - NO DART FALLBACKS *** +/// +/// Provides access to C++ tool calling functions: +/// - Parse tags from LLM output +/// - Format tools for system prompt +/// - Build initial and follow-up prompts +/// - Normalize JSON (fix unquoted keys) +class DartBridgeToolCalling { + // MARK: - Singleton + + /// Shared instance + static final DartBridgeToolCalling shared = DartBridgeToolCalling._(); + + DartBridgeToolCalling._(); + + // MARK: - State + + final _logger = SDKLogger('DartBridge.ToolCalling'); + DynamicLibrary? _lib; + + DynamicLibrary get lib { + _lib ??= PlatformLoader.loadCommons(); + return _lib!; + } + + // MARK: - Parse Tool Call (NO FALLBACK) + + /// Parse LLM output for tool calls using C++ implementation. + /// + /// *** THIS IS THE ONLY PARSING IMPLEMENTATION - NO DART FALLBACK *** + /// + /// Handles all edge cases: + /// - Missing closing tags (brace-matching) + /// - Unquoted JSON keys ({tool: "name"} โ†’ {"tool": "name"}) + /// - Multiple key naming conventions + /// - Tool name as key pattern + /// + /// [llmOutput] Raw LLM output text + /// Returns parsed result with tool call info + ToolCallParseResult parseToolCall(String llmOutput) { + try { + final parseFn = lib.lookupFunction< + Int32 Function(Pointer, Pointer), + int Function(Pointer, Pointer)>( + 'rac_tool_call_parse', + ); + + final freeFn = lib.lookupFunction< + Void Function(Pointer), + void Function(Pointer)>( + 'rac_tool_call_free', + ); + + final outputPtr = llmOutput.toNativeUtf8(); + final resultPtr = calloc(); + + try { + final rc = parseFn(outputPtr, resultPtr); + + if (rc != RAC_SUCCESS) { + return ToolCallParseResult( + hasToolCall: false, + cleanText: llmOutput, + callId: 0, + ); + } + + final result = resultPtr.ref; + final hasToolCall = result.hasToolCall == RAC_TRUE; + + String cleanText = llmOutput; + if (result.cleanText != nullptr) { + cleanText = result.cleanText.toDartString(); + } + + String? toolName; + Map? arguments; + int callId = 0; + + if (hasToolCall) { + if (result.toolName != nullptr) { + toolName = result.toolName.toDartString(); + } + + if (result.argumentsJson != nullptr) { + final argsJson = result.argumentsJson.toDartString(); + try { + arguments = jsonDecode(argsJson) as Map; + } catch (e) { + arguments = {}; + } + } + + callId = result.callId; + } + + freeFn(resultPtr); + + return ToolCallParseResult( + hasToolCall: hasToolCall, + cleanText: cleanText, + toolName: toolName, + arguments: arguments, + callId: callId, + ); + } finally { + calloc.free(outputPtr); + calloc.free(resultPtr); + } + } catch (e) { + _logger.error('parseToolCall failed: $e'); + return ToolCallParseResult( + hasToolCall: false, + cleanText: llmOutput, + callId: 0, + ); + } + } + + // ============================================================================= + // MARK: - Format Tools for Prompt (NO FALLBACK) + + /// Format tool definitions into a system prompt using C++ implementation + /// with a specific format. + /// + /// [toolsJson] JSON array of tool definitions + /// [formatName] Format name ("default", "lfm2") + /// Returns formatted system prompt string + String formatToolsPromptWithFormat(String toolsJson, String formatName) { + if (toolsJson.isEmpty || toolsJson == '[]') { + return ''; + } + + try { + final formatFn = lib.lookupFunction< + Int32 Function(Pointer, Pointer, Pointer>), + int Function(Pointer, Pointer, Pointer>)>( + 'rac_tool_call_format_prompt_json_with_format_name', + ); + + final racFreeFn = lib.lookupFunction), + void Function(Pointer)>('rac_free'); + + final toolsPtr = toolsJson.toNativeUtf8(); + final formatPtr = formatName.toNativeUtf8(); + final promptPtrPtr = calloc>(); + + try { + final rc = formatFn(toolsPtr, formatPtr, promptPtrPtr); + + if (rc != RAC_SUCCESS || promptPtrPtr.value == nullptr) { + _logger.error('formatToolsPromptWithFormat C++ returned error: $rc'); + return formatToolsPrompt(toolsJson); // Fallback to default + } + + final result = promptPtrPtr.value.toDartString(); + racFreeFn(promptPtrPtr.value.cast()); + return result; + } finally { + calloc.free(toolsPtr); + calloc.free(formatPtr); + calloc.free(promptPtrPtr); + } + } catch (e) { + _logger.error('formatToolsPromptWithFormat failed: $e'); + return formatToolsPrompt(toolsJson); // Fallback to default + } + } + + /// Format tool definitions into a system prompt using C++ implementation + /// (uses default format). + /// + /// [toolsJson] JSON array of tool definitions + /// Returns formatted system prompt string + String formatToolsPrompt(String toolsJson) { + if (toolsJson.isEmpty || toolsJson == '[]') { + return ''; + } + + try { + final formatFn = lib.lookupFunction< + Int32 Function(Pointer, Pointer>), + int Function(Pointer, Pointer>)>( + 'rac_tool_call_format_prompt_json', + ); + + final racFreeFn = lib.lookupFunction), + void Function(Pointer)>('rac_free'); + + final toolsPtr = toolsJson.toNativeUtf8(); + final promptPtrPtr = calloc>(); + + try { + final rc = formatFn(toolsPtr, promptPtrPtr); + + if (rc != RAC_SUCCESS || promptPtrPtr.value == nullptr) { + return ''; + } + + final result = promptPtrPtr.value.toDartString(); + racFreeFn(promptPtrPtr.value.cast()); + return result; + } finally { + calloc.free(toolsPtr); + calloc.free(promptPtrPtr); + } + } catch (e) { + _logger.error('formatToolsPrompt failed: $e'); + return ''; + } + } + + // MARK: - Build Initial Prompt (NO FALLBACK) + + /// Build initial prompt with tools and user query using C++ implementation. + /// + /// [userPrompt] The user's question/request + /// [toolsJson] JSON array of tool definitions + /// [optionsJson] Options as JSON (can be empty) + /// Returns complete formatted prompt + String buildInitialPrompt( + String userPrompt, + String toolsJson, { + String? optionsJson, + }) { + try { + final buildFn = lib.lookupFunction< + Int32 Function( + Pointer, + Pointer, + Pointer, + Pointer>, + ), + int Function( + Pointer, + Pointer, + Pointer, + Pointer>, + )>('rac_tool_call_build_initial_prompt'); + + final racFreeFn = lib.lookupFunction), + void Function(Pointer)>('rac_free'); + + final userPtr = userPrompt.toNativeUtf8(); + final toolsPtr = toolsJson.toNativeUtf8(); + final optionsPtr = calloc(); + final promptPtrPtr = calloc>(); + + // Set default options + optionsPtr.ref.maxToolCalls = 5; + optionsPtr.ref.autoExecute = RAC_TRUE; + optionsPtr.ref.temperature = 0.7; + optionsPtr.ref.maxTokens = 1024; + optionsPtr.ref.systemPrompt = nullptr; + optionsPtr.ref.replaceSystemPrompt = RAC_FALSE; + optionsPtr.ref.keepToolsAvailable = RAC_FALSE; + + try { + final rc = buildFn(userPtr, toolsPtr, optionsPtr, promptPtrPtr); + + if (rc != RAC_SUCCESS || promptPtrPtr.value == nullptr) { + return userPrompt; + } + + final result = promptPtrPtr.value.toDartString(); + racFreeFn(promptPtrPtr.value.cast()); + return result; + } finally { + calloc.free(userPtr); + calloc.free(toolsPtr); + calloc.free(optionsPtr); + calloc.free(promptPtrPtr); + } + } catch (e) { + _logger.error('buildInitialPrompt failed: $e'); + return userPrompt; + } + } + + // MARK: - Build Follow-up Prompt (NO FALLBACK) + + /// Build follow-up prompt after tool execution using C++ implementation. + /// + /// [originalPrompt] The original user prompt + /// [toolsPrompt] Formatted tools prompt (can be empty) + /// [toolName] Name of executed tool + /// [toolResultJson] Tool result as JSON + /// [keepToolsAvailable] Whether to keep tools in follow-up + /// Returns follow-up prompt string + String buildFollowupPrompt({ + required String originalPrompt, + String? toolsPrompt, + required String toolName, + required String toolResultJson, + bool keepToolsAvailable = false, + }) { + try { + final buildFn = lib.lookupFunction< + Int32 Function( + Pointer, + Pointer, + Pointer, + Pointer, + Int32, + Pointer>, + ), + int Function( + Pointer, + Pointer, + Pointer, + Pointer, + int, + Pointer>, + )>('rac_tool_call_build_followup_prompt'); + + final racFreeFn = lib.lookupFunction), + void Function(Pointer)>('rac_free'); + + final originalPtr = originalPrompt.toNativeUtf8(); + final toolsPromptPtr = + toolsPrompt != null ? toolsPrompt.toNativeUtf8() : nullptr; + final toolNamePtr = toolName.toNativeUtf8(); + final resultPtr = toolResultJson.toNativeUtf8(); + final promptPtrPtr = calloc>(); + + try { + final rc = buildFn( + originalPtr, + toolsPromptPtr, + toolNamePtr, + resultPtr, + keepToolsAvailable ? RAC_TRUE : RAC_FALSE, + promptPtrPtr, + ); + + if (rc != RAC_SUCCESS || promptPtrPtr.value == nullptr) { + return ''; + } + + final result = promptPtrPtr.value.toDartString(); + racFreeFn(promptPtrPtr.value.cast()); + return result; + } finally { + calloc.free(originalPtr); + if (toolsPromptPtr != nullptr) calloc.free(toolsPromptPtr); + calloc.free(toolNamePtr); + calloc.free(resultPtr); + calloc.free(promptPtrPtr); + } + } catch (e) { + _logger.error('buildFollowupPrompt failed: $e'); + return ''; + } + } + + // MARK: - JSON Normalization (NO FALLBACK) + + /// Normalize JSON by adding quotes around unquoted keys using C++ implementation. + /// + /// [jsonStr] Raw JSON possibly with unquoted keys + /// Returns normalized JSON string + String normalizeJson(String jsonStr) { + try { + final normalizeFn = lib.lookupFunction< + Int32 Function(Pointer, Pointer>), + int Function(Pointer, Pointer>)>( + 'rac_tool_call_normalize_json', + ); + + final racFreeFn = lib.lookupFunction), + void Function(Pointer)>('rac_free'); + + final inputPtr = jsonStr.toNativeUtf8(); + final outputPtrPtr = calloc>(); + + try { + final rc = normalizeFn(inputPtr, outputPtrPtr); + + if (rc != RAC_SUCCESS || outputPtrPtr.value == nullptr) { + return jsonStr; + } + + final result = outputPtrPtr.value.toDartString(); + racFreeFn(outputPtrPtr.value.cast()); + return result; + } finally { + calloc.free(inputPtr); + calloc.free(outputPtrPtr); + } + } catch (e) { + _logger.error('normalizeJson failed: $e'); + return jsonStr; + } + } +} diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart index 600ec0833..01ab2d55a 100644 --- a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart @@ -1012,6 +1012,60 @@ base class RacVadOnnxResultStruct extends Struct { external double probability; } +// ============================================================================= +// Tool Calling FFI Types (from rac_tool_calling.h) +// ============================================================================= + +/// Parsed tool call from LLM output - matches rac_tool_call_t +base class RacToolCallStruct extends Struct { + @Int32() + external int hasToolCall; + + external Pointer toolName; + + external Pointer argumentsJson; + + external Pointer cleanText; + + @Int64() + external int callId; +} + +/// Tool calling options - matches rac_tool_calling_options_t +base class RacToolCallingOptionsStruct extends Struct { + @Int32() + external int maxToolCalls; + + @Int32() + external int autoExecute; + + @Float() + external double temperature; + + @Int32() + external int maxTokens; + + external Pointer systemPrompt; + + @Int32() + external int replaceSystemPrompt; + + @Int32() + external int keepToolsAvailable; + + @Int32() + external int format; +} + +/// Tool parameter type enum values - matches rac_tool_param_type_t +abstract class RacToolParamType { + static const int string = 0; + static const int number = 1; + static const int boolean = 2; + static const int object = 3; + static const int array = 4; +} + // ============================================================================= // Backward Compatibility Aliases // ============================================================================= diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere_tool_calling.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere_tool_calling.dart new file mode 100644 index 000000000..aa68569d9 --- /dev/null +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere_tool_calling.dart @@ -0,0 +1,407 @@ +/// Tool Calling Extension for RunAnywhere SDK +/// +/// Provides tool calling (function calling) functionality for LLMs. +/// Allows LLMs to request external actions (API calls, device functions, etc.) +/// +/// Matches Swift SDK's RunAnywhere+ToolCalling.swift +library runanywhere_tool_calling; + +import 'dart:convert'; + +import 'package:runanywhere/foundation/logging/sdk_logger.dart'; +import 'package:runanywhere/native/dart_bridge.dart'; +import 'package:runanywhere/native/dart_bridge_tool_calling.dart'; +import 'package:runanywhere/public/runanywhere.dart'; +import 'package:runanywhere/public/types/generation_types.dart'; +import 'package:runanywhere/public/types/tool_calling_types.dart'; + +/// Tool calling extension for RunAnywhere +/// +/// Provides: +/// - Tool registration and management +/// - Tool-enabled generation with automatic execution +/// - Manual tool execution support +extension RunAnywhereToolCalling on RunAnywhere { + // Private static registry - stores tool executors by name + static final Map _toolExecutors = {}; + static final Map _toolDefinitions = {}; + static final _logger = SDKLogger('RunAnywhere.ToolCalling'); + + // ============================================================================ + // MARK: - Tool Registration + // ============================================================================ + + /// Register a tool with the SDK. + /// + /// [definition] Tool definition including name, description, and parameters + /// [executor] Async function to execute when the tool is called + /// + /// Example: + /// ```dart + /// RunAnywhereTools.registerTool( + /// ToolDefinition( + /// name: 'get_weather', + /// description: 'Get current weather for a location', + /// parameters: [ + /// ToolParameter( + /// name: 'location', + /// type: ToolParameterType.string, + /// description: 'City name or coordinates', + /// ), + /// ], + /// ), + /// (args) async { + /// final location = args['location']?.stringValue ?? 'Unknown'; + /// // Call weather API... + /// return {'temperature': NumberToolValue(72), 'condition': StringToolValue('Sunny')}; + /// }, + /// ); + /// ``` + static void registerTool(ToolDefinition definition, ToolExecutor executor) { + _toolDefinitions[definition.name] = definition; + _toolExecutors[definition.name] = executor; + _logger.info('Registered tool: ${definition.name}'); + } + + /// Unregister a tool by name + static void unregisterTool(String toolName) { + _toolDefinitions.remove(toolName); + _toolExecutors.remove(toolName); + _logger.info('Unregistered tool: $toolName'); + } + + /// Get all registered tool definitions + static List getRegisteredTools() { + return List.unmodifiable(_toolDefinitions.values.toList()); + } + + /// Clear all registered tools + static void clearTools() { + _toolDefinitions.clear(); + _toolExecutors.clear(); + _logger.info('Cleared all registered tools'); + } + + // ============================================================================ + // MARK: - Tool Execution + // ============================================================================ + + /// Execute a tool call manually. + /// + /// [toolCall] The tool call to execute + /// Returns ToolResult with success/failure and result data + static Future executeTool(ToolCall toolCall) async { + final executor = _toolExecutors[toolCall.toolName]; + + if (executor == null) { + return ToolResult( + toolName: toolCall.toolName, + success: false, + error: 'Tool not found: ${toolCall.toolName}', + callId: toolCall.callId, + ); + } + + try { + _logger.debug('Executing tool: ${toolCall.toolName}'); + final result = await executor(toolCall.arguments); + _logger.debug('Tool ${toolCall.toolName} completed successfully'); + + return ToolResult( + toolName: toolCall.toolName, + success: true, + result: result, + callId: toolCall.callId, + ); + } catch (e) { + _logger.error('Tool ${toolCall.toolName} failed: $e'); + return ToolResult( + toolName: toolCall.toolName, + success: false, + error: e.toString(), + callId: toolCall.callId, + ); + } + } + + // ============================================================================ + // MARK: - Tool-Enabled Generation + // ============================================================================ + + /// Generate text with tool calling support. + /// + /// This is the main entry point for tool-enabled generation. + /// Handles the full tool calling loop: + /// 1. Format tools into system prompt + /// 2. Generate LLM response + /// 3. Parse tool calls from output + /// 4. Execute tools (if autoExecute is true) + /// 5. Continue generation with tool results + /// 6. Repeat until no more tool calls or max iterations reached + /// + /// [prompt] User's question or request + /// [options] Tool calling options (optional) + /// + /// Example: + /// ```dart + /// final result = await RunAnywhereTools.generateWithTools( + /// 'What is the weather in San Francisco?', + /// ); + /// print(result.text); // "The weather in San Francisco is 72ยฐF and Sunny." + /// print(result.toolCalls); // [ToolCall(name: 'get_weather', ...)] + /// ``` + static Future generateWithTools( + String prompt, { + ToolCallingOptions? options, + }) async { + final opts = options ?? const ToolCallingOptions(); + final tools = opts.tools ?? getRegisteredTools(); + final formatName = opts.formatName; + + if (tools.isEmpty) { + // No tools - just do regular generation + final result = await RunAnywhere.generate(prompt); + return ToolCallingResult( + text: result.text, + toolCalls: [], + toolResults: [], + isComplete: true, + ); + } + + // Build tools JSON + final toolsJson = toolsToJson(tools); + _logger.debug('Tools JSON: $toolsJson'); + _logger.debug('Using tool call format: $formatName'); + + // Build initial prompt with tools using the specified format + final toolsPrompt = DartBridgeToolCalling.shared.formatToolsPromptWithFormat( + toolsJson, + formatName, + ); + + // Build the full prompt with system instructions and user query + final formattedPrompt = '$toolsPrompt\n\nUser: $prompt'; + _logger.debug('Formatted prompt: ${formattedPrompt.substring(0, formattedPrompt.length.clamp(0, 200))}...'); + + // Track all tool calls and results + final allToolCalls = []; + final allToolResults = []; + + var currentPrompt = formattedPrompt; + var iterations = 0; + final maxIterations = opts.maxToolCalls; + + while (iterations < maxIterations) { + iterations++; + + // Lower temperature for more consistent tool calling behavior + final genOptions = LLMGenerationOptions( + maxTokens: opts.maxTokens ?? 1024, + temperature: opts.temperature ?? 0.3, + ); + + // Use streaming like Swift does, then collect all tokens + final streamResult = await RunAnywhere.generateStream(currentPrompt, options: genOptions); + final buffer = StringBuffer(); + await for (final token in streamResult.stream) { + buffer.write(token); + } + final responseText = buffer.toString(); + + _logger.debug('LLM output (iter $iterations): ${responseText.substring(0, responseText.length.clamp(0, 200))}...'); + + // Parse for tool calls using C++ bridge (auto-detection like Swift) + final parseResult = DartBridgeToolCalling.shared.parseToolCall(responseText); + + if (!parseResult.hasToolCall || parseResult.toolName == null) { + // No tool call - return final result + return ToolCallingResult( + text: parseResult.cleanText, + toolCalls: allToolCalls, + toolResults: allToolResults, + isComplete: true, + ); + } + + // Create tool call + final toolCall = ToolCall( + toolName: parseResult.toolName!, + arguments: parseResult.arguments != null + ? dynamicMapToToolValueMap(parseResult.arguments!) + : {}, + callId: parseResult.callId.toString(), + ); + allToolCalls.add(toolCall); + + _logger.info('Tool call detected: ${toolCall.toolName}'); + + if (!opts.autoExecute) { + // Return for manual execution + return ToolCallingResult( + text: parseResult.cleanText, + toolCalls: allToolCalls, + toolResults: allToolResults, + isComplete: false, + ); + } + + // Execute the tool + final toolResult = await executeTool(toolCall); + allToolResults.add(toolResult); + + // Build follow-up prompt with tool result + final resultJson = toolResult.result != null + ? toolResultToJsonString(toolResult.result!) + : '{"error": "${toolResult.error ?? 'Unknown error'}"}'; + + currentPrompt = DartBridgeToolCalling.shared.buildFollowupPrompt( + originalPrompt: prompt, + toolsPrompt: opts.keepToolsAvailable + ? DartBridgeToolCalling.shared.formatToolsPrompt(toolsJson) + : null, + toolName: toolCall.toolName, + toolResultJson: resultJson, + keepToolsAvailable: opts.keepToolsAvailable, + ); + + _logger.debug('Follow-up prompt: ${currentPrompt.substring(0, currentPrompt.length.clamp(0, 200))}...'); + } + + // Max iterations reached - return what we have + _logger.warning('Max tool call iterations ($maxIterations) reached'); + return ToolCallingResult( + text: '', + toolCalls: allToolCalls, + toolResults: allToolResults, + isComplete: true, + ); + } + + /// Continue generation after manual tool execution. + /// + /// Use this when autoExecute is false and you've executed tools manually. + /// + /// [originalPrompt] The original user prompt + /// [toolResult] Result from manual tool execution + /// [options] Tool calling options + static Future continueWithToolResult( + String originalPrompt, + ToolResult toolResult, { + ToolCallingOptions? options, + }) async { + final opts = options ?? const ToolCallingOptions(); + final tools = opts.tools ?? getRegisteredTools(); + final toolsJson = toolsToJson(tools); + + final resultJson = toolResult.result != null + ? toolResultToJsonString(toolResult.result!) + : '{"error": "${toolResult.error ?? 'Unknown error'}"}'; + + final followupPrompt = DartBridgeToolCalling.shared.buildFollowupPrompt( + originalPrompt: originalPrompt, + toolsPrompt: opts.keepToolsAvailable + ? DartBridgeToolCalling.shared.formatToolsPrompt(toolsJson) + : null, + toolName: toolResult.toolName, + toolResultJson: resultJson, + keepToolsAvailable: opts.keepToolsAvailable, + ); + + // Continue with the follow-up + return generateWithTools(followupPrompt, options: opts); + } + + // ============================================================================ + // MARK: - Helper Functions + // ============================================================================ + + /// Format tools for system prompt. + /// + /// Useful for inspecting or customizing tool prompts. + static String formatToolsForPrompt([List? tools]) { + final toolList = tools ?? getRegisteredTools(); + if (toolList.isEmpty) return ''; + + final toolsJson = toolsToJson(toolList); + return DartBridgeToolCalling.shared.formatToolsPrompt(toolsJson); + } + + /// Parse tool call from LLM output. + /// + /// Useful for manual parsing without automatic execution. + static ToolCall? parseToolCall(String llmOutput) { + final result = DartBridgeToolCalling.shared.parseToolCall(llmOutput); + + if (!result.hasToolCall || result.toolName == null) { + return null; + } + + return ToolCall( + toolName: result.toolName!, + arguments: result.arguments != null + ? dynamicMapToToolValueMap(result.arguments!) + : {}, + callId: result.callId.toString(), + ); + } +} + +/// Convenience class for tool calling without extension syntax +/// +/// Use this for simpler imports: +/// ```dart +/// import 'package:runanywhere/public/runanywhere_tool_calling.dart'; +/// +/// RunAnywhereTools.registerTool(...); +/// final result = await RunAnywhereTools.generateWithTools('...'); +/// ``` +class RunAnywhereTools { + RunAnywhereTools._(); + + /// Register a tool with the SDK + static void registerTool(ToolDefinition definition, ToolExecutor executor) => + RunAnywhereToolCalling.registerTool(definition, executor); + + /// Unregister a tool by name + static void unregisterTool(String toolName) => + RunAnywhereToolCalling.unregisterTool(toolName); + + /// Get all registered tool definitions + static List getRegisteredTools() => + RunAnywhereToolCalling.getRegisteredTools(); + + /// Clear all registered tools + static void clearTools() => RunAnywhereToolCalling.clearTools(); + + /// Execute a tool call manually + static Future executeTool(ToolCall toolCall) => + RunAnywhereToolCalling.executeTool(toolCall); + + /// Generate text with tool calling support + static Future generateWithTools( + String prompt, { + ToolCallingOptions? options, + }) => + RunAnywhereToolCalling.generateWithTools(prompt, options: options); + + /// Continue generation after manual tool execution + static Future continueWithToolResult( + String originalPrompt, + ToolResult toolResult, { + ToolCallingOptions? options, + }) => + RunAnywhereToolCalling.continueWithToolResult( + originalPrompt, + toolResult, + options: options, + ); + + /// Format tools for system prompt + static String formatToolsForPrompt([List? tools]) => + RunAnywhereToolCalling.formatToolsForPrompt(tools); + + /// Parse tool call from LLM output + static ToolCall? parseToolCall(String llmOutput) => + RunAnywhereToolCalling.parseToolCall(llmOutput); +} diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/tool_calling_types.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/tool_calling_types.dart new file mode 100644 index 000000000..5f4c3fbcd --- /dev/null +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/tool_calling_types.dart @@ -0,0 +1,365 @@ +/// Tool Calling Types for RunAnywhere SDK +/// +/// Type definitions for tool calling (function calling) functionality. +/// Allows LLMs to request external actions (API calls, device functions, etc.) +/// +/// Mirrors Swift SDK's ToolCallingTypes.swift +library tool_calling_types; + +import 'dart:convert'; + +// ============================================================================= +// TOOL CALL FORMAT NAMES +// ============================================================================= + +/// Constants for tool call format names. +/// +/// The format logic is handled in C++ commons (single source of truth). +/// Mirrors Swift SDK's ToolCallFormatName enum. +abstract class ToolCallFormatName { + /// JSON format: `{"tool":"name","arguments":{...}}` + /// Use for most general-purpose models (Llama, Qwen, Mistral, etc.) + static const String defaultFormat = 'default'; + + /// Liquid AI format: `<|tool_call_start|>[func(args)]<|tool_call_end|>` + /// Use for LFM2-Tool models + static const String lfm2 = 'lfm2'; +} + +// ============================================================================= +// TOOL VALUE - Type-safe JSON representation +// ============================================================================= + +/// A type-safe representation of JSON values for tool arguments and results. +/// Avoids using `dynamic` while supporting all JSON types. +sealed class ToolValue { + const ToolValue(); + + // Convenience value extraction + String? get stringValue => this is StringToolValue ? (this as StringToolValue).value : null; + double? get numberValue => this is NumberToolValue ? (this as NumberToolValue).value : null; + int? get intValue => numberValue?.toInt(); + bool? get boolValue => this is BoolToolValue ? (this as BoolToolValue).value : null; + List? get arrayValue => + this is ArrayToolValue ? (this as ArrayToolValue).value : null; + Map? get objectValue => + this is ObjectToolValue ? (this as ObjectToolValue).value : null; + bool get isNull => this is NullToolValue; + + /// Convert to JSON-compatible dynamic value + dynamic toJson() => switch (this) { + StringToolValue(value: var v) => v, + NumberToolValue(value: var v) => v, + BoolToolValue(value: var v) => v, + ArrayToolValue(value: var v) => v.map((e) => e.toJson()).toList(), + ObjectToolValue(value: var v) => v.map((k, val) => MapEntry(k, val.toJson())), + NullToolValue() => null, + }; + + /// Create from any JSON-compatible value + static ToolValue from(dynamic value) => switch (value) { + null => const NullToolValue(), + String s => StringToolValue(s), + num n => NumberToolValue(n.toDouble()), + bool b => BoolToolValue(b), + List l => ArrayToolValue(l.map(from).toList()), + Map m => ObjectToolValue(m.map((k, v) => MapEntry(k.toString(), from(v)))), + _ => StringToolValue(value.toString()), + }; +} + +class StringToolValue extends ToolValue { + final String value; + const StringToolValue(this.value); +} + +class NumberToolValue extends ToolValue { + final double value; + const NumberToolValue(this.value); +} + +class BoolToolValue extends ToolValue { + final bool value; + const BoolToolValue(this.value); +} + +class ArrayToolValue extends ToolValue { + final List value; + const ArrayToolValue(this.value); +} + +class ObjectToolValue extends ToolValue { + final Map value; + const ObjectToolValue(this.value); +} + +class NullToolValue extends ToolValue { + const NullToolValue(); +} + +// ============================================================================= +// PARAMETER TYPES +// ============================================================================= + +/// Supported parameter types for tool arguments +enum ToolParameterType { + string('string'), + number('number'), + boolean('boolean'), + object('object'), + array('array'); + + final String value; + const ToolParameterType(this.value); + + static ToolParameterType fromString(String value) => switch (value.toLowerCase()) { + 'string' => ToolParameterType.string, + 'number' => ToolParameterType.number, + 'boolean' => ToolParameterType.boolean, + 'object' => ToolParameterType.object, + 'array' => ToolParameterType.array, + _ => ToolParameterType.string, + }; +} + +/// A single parameter definition for a tool +class ToolParameter { + /// Parameter name + final String name; + + /// Data type of the parameter + final ToolParameterType type; + + /// Human-readable description + final String description; + + /// Whether this parameter is required + final bool required; + + /// Allowed values (for enum-like parameters) + final List? enumValues; + + const ToolParameter({ + required this.name, + required this.type, + required this.description, + this.required = true, + this.enumValues, + }); + + Map toJson() => { + 'name': name, + 'type': type.value, + 'description': description, + 'required': required, + if (enumValues != null) 'enumValues': enumValues, + }; +} + +// ============================================================================= +// TOOL DEFINITION TYPES +// ============================================================================= + +/// Definition of a tool that the LLM can use +class ToolDefinition { + /// Unique name of the tool (e.g., "get_weather") + final String name; + + /// Human-readable description of what the tool does + final String description; + + /// Parameters the tool accepts + final List parameters; + + /// Category for organizing tools (optional) + final String? category; + + const ToolDefinition({ + required this.name, + required this.description, + required this.parameters, + this.category, + }); + + Map toJson() => { + 'name': name, + 'description': description, + 'parameters': parameters.map((p) => p.toJson()).toList(), + if (category != null) 'category': category, + }; +} + +// ============================================================================= +// TOOL CALL TYPES (LLM requesting to use a tool) +// ============================================================================= + +/// A request from the LLM to execute a tool +class ToolCall { + /// Name of the tool to execute + final String toolName; + + /// Arguments to pass to the tool + final Map arguments; + + /// Unique ID for this tool call (for tracking) + final String? callId; + + const ToolCall({ + required this.toolName, + required this.arguments, + this.callId, + }); + + /// Get a string argument by name + String? getString(String key) => arguments[key]?.stringValue; + + /// Get a number argument by name + double? getNumber(String key) => arguments[key]?.numberValue; + + /// Get a bool argument by name + bool? getBool(String key) => arguments[key]?.boolValue; +} + +// ============================================================================= +// TOOL RESULT TYPES (Result after execution) +// ============================================================================= + +/// Result of executing a tool +class ToolResult { + /// Name of the tool that was executed + final String toolName; + + /// Whether execution was successful + final bool success; + + /// Result data (if successful) + final Map? result; + + /// Error message (if failed) + final String? error; + + /// The original call ID (for tracking) + final String? callId; + + const ToolResult({ + required this.toolName, + required this.success, + this.result, + this.error, + this.callId, + }); + + Map toJson() => { + 'toolName': toolName, + 'success': success, + if (result != null) 'result': result!.map((k, v) => MapEntry(k, v.toJson())), + if (error != null) 'error': error, + if (callId != null) 'callId': callId, + }; +} + +// ============================================================================= +// TOOL EXECUTOR TYPES +// ============================================================================= + +/// Function type for tool executors. +/// Takes arguments as strongly-typed ToolValue map, returns result map. +typedef ToolExecutor = Future> Function(Map args); + +// ============================================================================= +// TOOL CALLING OPTIONS +// ============================================================================= + +/// Options for tool-enabled generation +class ToolCallingOptions { + /// Available tools for this generation (if not provided, uses registered tools) + final List? tools; + + /// Maximum number of tool calls allowed in one conversation turn (default: 5) + final int maxToolCalls; + + /// Whether to automatically execute tools or return them for manual execution (default: true) + final bool autoExecute; + + /// Temperature for generation + final double? temperature; + + /// Maximum tokens to generate + final int? maxTokens; + + /// System prompt to use (will be merged with tool instructions by default) + final String? systemPrompt; + + /// If true, replaces the system prompt entirely instead of appending tool instructions + final bool replaceSystemPrompt; + + /// If true, keeps tool definitions available after the first tool call + final bool keepToolsAvailable; + + /// Tool calling format name (e.g., "default", "lfm2") + /// Different models are trained on different tool calling formats. + /// - "default": Standard JSON format for general-purpose models + /// - "lfm2": Pythonic format for LFM2-Tool models + final String formatName; + + const ToolCallingOptions({ + this.tools, + this.maxToolCalls = 5, + this.autoExecute = true, + this.temperature, + this.maxTokens, + this.systemPrompt, + this.replaceSystemPrompt = false, + this.keepToolsAvailable = false, + this.formatName = ToolCallFormatName.defaultFormat, + }); +} + +// ============================================================================= +// TOOL CALLING RESULT TYPES +// ============================================================================= + +/// Result of a generation that may include tool calls +class ToolCallingResult { + /// The final text response + final String text; + + /// Any tool calls the LLM made + final List toolCalls; + + /// Results of executed tools (if autoExecute was true) + final List toolResults; + + /// Whether the response is complete or waiting for tool results + final bool isComplete; + + /// Conversation ID for continuing with tool results + final String? conversationId; + + const ToolCallingResult({ + required this.text, + required this.toolCalls, + required this.toolResults, + required this.isComplete, + this.conversationId, + }); +} + +// ============================================================================= +// HELPER FUNCTIONS +// ============================================================================= + +/// Serialize tools to JSON string +String toolsToJson(List tools) { + return jsonEncode(tools.map((t) => t.toJson()).toList()); +} + +/// Convert Map to JSON string +String toolResultToJsonString(Map result) { + return jsonEncode(result.map((k, v) => MapEntry(k, v.toJson()))); +} + +/// Convert Map to Map +Map dynamicMapToToolValueMap(Map map) { + return map.map((k, v) => MapEntry(k, ToolValue.from(v))); +} diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/types.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/types.dart index 4449a9eac..1111ad66c 100644 --- a/sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/types.dart +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/types.dart @@ -8,4 +8,5 @@ export 'configuration_types.dart'; export 'download_types.dart'; export 'generation_types.dart'; export 'message_types.dart'; +export 'tool_calling_types.dart'; export 'voice_agent_types.dart'; diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/runanywhere.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/runanywhere.dart index 56393008f..594990b6d 100644 --- a/sdk/runanywhere-flutter/packages/runanywhere/lib/runanywhere.dart +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/runanywhere.dart @@ -28,4 +28,6 @@ export 'public/extensions/runanywhere_frameworks.dart'; export 'public/extensions/runanywhere_logging.dart'; export 'public/extensions/runanywhere_storage.dart'; export 'public/runanywhere.dart'; +export 'public/runanywhere_tool_calling.dart'; +export 'public/types/tool_calling_types.dart'; export 'public/types/types.dart' hide SupabaseConfig; diff --git a/sdk/runanywhere-kotlin/modules/runanywhere-core-llamacpp/build.gradle.kts b/sdk/runanywhere-kotlin/modules/runanywhere-core-llamacpp/build.gradle.kts index 7ee6f723f..5ef3ab96b 100644 --- a/sdk/runanywhere-kotlin/modules/runanywhere-core-llamacpp/build.gradle.kts +++ b/sdk/runanywhere-kotlin/modules/runanywhere-core-llamacpp/build.gradle.kts @@ -163,6 +163,14 @@ android { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } + // Prevent duplicate native library conflicts during test builds + jniLibs { + pickFirsts += setOf( + "lib/arm64-v8a/libomp.so", + "lib/arm64-v8a/libc++_shared.so", + "lib/arm64-v8a/librac_commons.so" + ) + } } // ========================================================================== diff --git a/sdk/runanywhere-kotlin/modules/runanywhere-core-onnx/build.gradle.kts b/sdk/runanywhere-kotlin/modules/runanywhere-core-onnx/build.gradle.kts index 001c241e5..dfa65b1fa 100644 --- a/sdk/runanywhere-kotlin/modules/runanywhere-core-onnx/build.gradle.kts +++ b/sdk/runanywhere-kotlin/modules/runanywhere-core-onnx/build.gradle.kts @@ -172,6 +172,14 @@ android { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } + // Prevent duplicate native library conflicts during test builds + jniLibs { + pickFirsts += setOf( + "lib/arm64-v8a/libomp.so", + "lib/arm64-v8a/libc++_shared.so", + "lib/arm64-v8a/librac_commons.so" + ) + } } // ========================================================================== diff --git a/sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/extensions/LLM/ToolCallingTypes.kt b/sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/extensions/LLM/ToolCallingTypes.kt new file mode 100644 index 000000000..5d123c58f --- /dev/null +++ b/sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/extensions/LLM/ToolCallingTypes.kt @@ -0,0 +1,301 @@ +/* + * Copyright 2026 RunAnywhere SDK + * SPDX-License-Identifier: Apache-2.0 + * + * Type definitions for Tool Calling functionality. + * Allows LLMs to request external actions (API calls, device functions, etc.) + * + * Mirrors Swift SDK's ToolCallingTypes.swift + */ + +package com.runanywhere.sdk.public.extensions.LLM + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement + +// ============================================================================= +// TOOL VALUE - Type-safe JSON representation +// ============================================================================= + +/** + * A type-safe representation of JSON values for tool arguments and results. + * Avoids using `Any` while supporting all JSON types. + */ +@Serializable +sealed class ToolValue { + @Serializable + data class StringValue(val value: String) : ToolValue() + + @Serializable + data class NumberValue(val value: Double) : ToolValue() + + @Serializable + data class BoolValue(val value: Boolean) : ToolValue() + + @Serializable + data class ArrayValue(val value: List) : ToolValue() + + @Serializable + data class ObjectValue(val value: Map) : ToolValue() + + @Serializable + object NullValue : ToolValue() + + // Convenience value extraction + val stringValue: String? get() = (this as? StringValue)?.value + val numberValue: Double? get() = (this as? NumberValue)?.value + val intValue: Int? get() = numberValue?.toInt() + val boolValue: Boolean? get() = (this as? BoolValue)?.value + val arrayValue: List? get() = (this as? ArrayValue)?.value + val objectValue: Map? get() = (this as? ObjectValue)?.value + val isNull: Boolean get() = this is NullValue + + companion object { + fun string(value: String) = StringValue(value) + fun number(value: Double) = NumberValue(value) + fun number(value: Int) = NumberValue(value.toDouble()) + fun bool(value: Boolean) = BoolValue(value) + fun array(value: List) = ArrayValue(value) + fun obj(value: Map) = ObjectValue(value) + fun nullValue() = NullValue + + /** + * Convert Any to ToolValue + */ + fun from(value: Any?): ToolValue = when (value) { + null -> NullValue + is String -> StringValue(value) + is Number -> NumberValue(value.toDouble()) + is Boolean -> BoolValue(value) + is List<*> -> ArrayValue(value.map { from(it) }) + is Map<*, *> -> ObjectValue(value.entries.associate { (k, v) -> + k.toString() to from(v) + }) + else -> StringValue(value.toString()) + } + } +} + +// ============================================================================= +// PARAMETER TYPES +// ============================================================================= + +/** + * Supported parameter types for tool arguments + */ +enum class ToolParameterType(val value: String) { + STRING("string"), + NUMBER("number"), + BOOLEAN("boolean"), + OBJECT("object"), + ARRAY("array"); + + companion object { + fun fromString(value: String): ToolParameterType = when (value.lowercase()) { + "string" -> STRING + "number" -> NUMBER + "boolean" -> BOOLEAN + "object" -> OBJECT + "array" -> ARRAY + else -> STRING + } + } +} + +/** + * A single parameter definition for a tool + */ +data class ToolParameter( + /** Parameter name */ + val name: String, + /** Data type of the parameter */ + val type: ToolParameterType, + /** Human-readable description */ + val description: String, + /** Whether this parameter is required */ + val required: Boolean = true, + /** Allowed values (for enum-like parameters) */ + val enumValues: List? = null +) + +// ============================================================================= +// TOOL DEFINITION TYPES +// ============================================================================= + +/** + * Definition of a tool that the LLM can use + */ +data class ToolDefinition( + /** Unique name of the tool (e.g., "get_weather") */ + val name: String, + /** Human-readable description of what the tool does */ + val description: String, + /** Parameters the tool accepts */ + val parameters: List, + /** Category for organizing tools (optional) */ + val category: String? = null +) + +// ============================================================================= +// TOOL CALL TYPES (LLM requesting to use a tool) +// ============================================================================= + +/** + * A request from the LLM to execute a tool + */ +data class ToolCall( + /** Name of the tool to execute */ + val toolName: String, + /** Arguments to pass to the tool */ + val arguments: Map, + /** Unique ID for this tool call (for tracking) */ + val callId: String? = null +) { + /** Get a string argument by name */ + fun getString(key: String): String? = arguments[key]?.stringValue + + /** Get a number argument by name */ + fun getNumber(key: String): Double? = arguments[key]?.numberValue + + /** Get a bool argument by name */ + fun getBool(key: String): Boolean? = arguments[key]?.boolValue +} + +// ============================================================================= +// TOOL RESULT TYPES (Result after execution) +// ============================================================================= + +/** + * Result of executing a tool + */ +data class ToolResult( + /** Name of the tool that was executed */ + val toolName: String, + /** Whether execution was successful */ + val success: Boolean, + /** Result data (if successful) */ + val result: Map? = null, + /** Error message (if failed) */ + val error: String? = null, + /** The original call ID (for tracking) */ + val callId: String? = null +) + +// ============================================================================= +// TOOL EXECUTOR TYPES +// ============================================================================= + +/** + * Function type for tool executors. + * Takes arguments as strongly-typed ToolValue map, returns result map. + */ +typealias ToolExecutor = suspend (Map) -> Map + +/** + * A registered tool with its definition and executor + */ +internal data class RegisteredTool( + val definition: ToolDefinition, + val executor: ToolExecutor +) + +// ============================================================================= +// TOOL CALL FORMAT NAMES +// ============================================================================= + +/** + * Format names for tool calling output (internal string constants). + * Used for C++ bridge communication. + */ +internal object ToolCallFormatName { + const val DEFAULT = "default" + const val LFM2 = "lfm2" +} + +/** + * Tool calling format types. + * Each format specifies how tool calls are formatted in the LLM prompt. + * + * The format logic is handled in C++ commons (single source of truth). + */ +sealed class ToolCallFormat { + /** + * Default format using XML-style tags. + * JSON format: `{"tool":"name","arguments":{...}}` + * Use for most general-purpose models (Llama, Qwen, Mistral, etc.) + */ + data object Default : ToolCallFormat() + + /** + * LFM2 format for Liquid AI models. + * Liquid AI format: `<|tool_call_start|>[func(args)]<|tool_call_end|>` + * Use for LFM2-Tool models. + */ + data object LFM2 : ToolCallFormat() + + /** Get the string representation for C++ bridge */ + fun toFormatName(): String = when (this) { + is Default -> ToolCallFormatName.DEFAULT + is LFM2 -> ToolCallFormatName.LFM2 + } + + companion object { + /** Convert from format name string (for deserialization) */ + fun fromFormatName(name: String?): ToolCallFormat = when (name) { + ToolCallFormatName.LFM2 -> LFM2 + else -> Default + } + } +} + +// ============================================================================= +// TOOL CALLING OPTIONS +// ============================================================================= + +/** + * Options for tool-enabled generation + */ +data class ToolCallingOptions( + /** Available tools for this generation (if not provided, uses registered tools) */ + val tools: List? = null, + /** Maximum number of tool calls allowed in one conversation turn (default: 5) */ + val maxToolCalls: Int = 5, + /** Whether to automatically execute tools or return them for manual execution (default: true) */ + val autoExecute: Boolean = true, + /** Temperature for generation */ + val temperature: Float? = null, + /** Maximum tokens to generate */ + val maxTokens: Int? = null, + /** System prompt to use (will be merged with tool instructions by default) */ + val systemPrompt: String? = null, + /** If true, replaces the system prompt entirely instead of appending tool instructions */ + val replaceSystemPrompt: Boolean = false, + /** If true, keeps tool definitions available after the first tool call */ + val keepToolsAvailable: Boolean = false, + /** + * Format for tool calls. + * Use [ToolCallFormat.LFM2] for LFM2-Tool models (Liquid AI). + * Default: [ToolCallFormat.Default] which uses JSON-based format suitable for most models. + */ + val format: ToolCallFormat = ToolCallFormat.Default +) + +// ============================================================================= +// TOOL CALLING RESULT TYPES +// ============================================================================= + +/** + * Result of a generation that may include tool calls + */ +data class ToolCallingResult( + /** The final text response */ + val text: String, + /** Any tool calls the LLM made */ + val toolCalls: List, + /** Results of executed tools (if autoExecute was true) */ + val toolResults: List, + /** Whether the response is complete or waiting for tool results */ + val isComplete: Boolean, + /** Conversation ID for continuing with tool results */ + val conversationId: String? = null +) diff --git a/sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeToolCalling.kt b/sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeToolCalling.kt new file mode 100644 index 000000000..125ea411c --- /dev/null +++ b/sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeToolCalling.kt @@ -0,0 +1,313 @@ +/* + * Copyright 2026 RunAnywhere SDK + * SPDX-License-Identifier: Apache-2.0 + * + * C++ bridge for tool calling functionality. + * + * *** SINGLE SOURCE OF TRUTH FOR TOOL CALLING LOGIC *** + * All parsing and prompt formatting is done in C++ (rac_tool_calling.h). + * This bridge is a THIN WRAPPER - no parsing logic in Kotlin. + * + * Platform SDKs handle ONLY: + * - Tool registry (Kotlin closures) + * - Tool execution (Kotlin async calls) + */ + +package com.runanywhere.sdk.foundation.bridge.extensions + +import com.runanywhere.sdk.foundation.SDKLogger +import com.runanywhere.sdk.native.bridge.RunAnywhereBridge +import com.runanywhere.sdk.public.extensions.LLM.* +import kotlinx.serialization.json.* + +/** + * Tool calling bridge to C++ implementation. + * + * *** ALL PARSING LOGIC IS IN C++ - NO KOTLIN FALLBACKS *** + */ +object CppBridgeToolCalling { + private const val TAG = "CppBridgeToolCalling" + private val logger = SDKLogger(TAG) + + /** + * Parsed tool call result from C++ + */ + data class ParseResult( + val hasToolCall: Boolean, + val cleanText: String, + val toolName: String?, + val argumentsJson: String?, + val callId: Long + ) + + // ======================================================================== + // PARSE TOOL CALL (NO FALLBACK) + // ======================================================================== + + /** + * Parse LLM output for tool calls using C++ implementation. + * + * *** THIS IS THE ONLY PARSING IMPLEMENTATION - NO KOTLIN FALLBACK *** + * + * @param llmOutput Raw LLM output text + * @return Parsed result with tool call info + */ + fun parseToolCall(llmOutput: String): ParseResult { + val resultJson = RunAnywhereBridge.racToolCallParse(llmOutput) + ?: return ParseResult( + hasToolCall = false, + cleanText = llmOutput, + toolName = null, + argumentsJson = null, + callId = 0 + ) + + return try { + val json = Json.parseToJsonElement(resultJson).jsonObject + ParseResult( + hasToolCall = json["hasToolCall"]?.jsonPrimitive?.boolean ?: false, + cleanText = json["cleanText"]?.jsonPrimitive?.content ?: llmOutput, + toolName = json["toolName"]?.jsonPrimitive?.contentOrNull, + argumentsJson = json["argumentsJson"]?.toString(), + callId = json["callId"]?.jsonPrimitive?.longOrNull ?: 0 + ) + } catch (e: Exception) { + logger.error("Failed to parse tool call result: ${e.message}") + ParseResult( + hasToolCall = false, + cleanText = llmOutput, + toolName = null, + argumentsJson = null, + callId = 0 + ) + } + } + + /** + * Parse LLM output and return a ToolCall object if found. + * + * @param llmOutput Raw LLM output text + * @return Pair of (cleanText, toolCall) where toolCall is null if none found + */ + fun parseToolCallToObject(llmOutput: String): Pair { + val result = parseToolCall(llmOutput) + + if (!result.hasToolCall || result.toolName == null) { + return Pair(result.cleanText, null) + } + + val arguments = parseArgumentsJson(result.argumentsJson ?: "{}") + + return Pair( + result.cleanText, + ToolCall( + toolName = result.toolName, + arguments = arguments, + callId = "call_${result.callId}" + ) + ) + } + + // ======================================================================== + // FORMAT TOOLS FOR PROMPT (NO FALLBACK) + // ======================================================================== + + /** + * Format tool definitions into a system prompt using C++ implementation. + * + * @param tools List of tool definitions + * @param format Tool calling format type. See [ToolCallFormat]. + * @return Formatted system prompt string + */ + fun formatToolsForPrompt( + tools: List, + format: ToolCallFormat = ToolCallFormat.Default + ): String { + if (tools.isEmpty()) return "" + + val toolsJson = serializeToolsToJson(tools) + // Convert to string at JNI boundary - C++ handles the format logic + val formatString = format.toFormatName() + return RunAnywhereBridge.racToolCallFormatPromptJsonWithFormatName(toolsJson, formatString) ?: "" + } + + // ======================================================================== + // BUILD INITIAL PROMPT (NO FALLBACK) + // ======================================================================== + + /** + * Build the initial prompt with tools and user query using C++ implementation. + * + * @param userPrompt The user's question/request + * @param tools List of tool definitions + * @param options Tool calling options + * @return Complete formatted prompt + */ + fun buildInitialPrompt( + userPrompt: String, + tools: List, + options: ToolCallingOptions + ): String { + val toolsJson = serializeToolsToJson(tools) + val optionsJson = serializeOptionsToJson(options) + + return RunAnywhereBridge.racToolCallBuildInitialPrompt( + userPrompt, + toolsJson, + optionsJson + ) ?: userPrompt + } + + // ======================================================================== + // BUILD FOLLOW-UP PROMPT (NO FALLBACK) + // ======================================================================== + + /** + * Build follow-up prompt after tool execution using C++ implementation. + * + * @param originalPrompt The original user prompt + * @param toolsPrompt The formatted tools prompt (null if not keeping tools) + * @param toolName Name of the tool that was executed + * @param toolResultJson JSON string of the tool result + * @param keepToolsAvailable Whether to include tool definitions + * @return Follow-up prompt string + */ + fun buildFollowupPrompt( + originalPrompt: String, + toolsPrompt: String?, + toolName: String, + toolResultJson: String, + keepToolsAvailable: Boolean + ): String { + return RunAnywhereBridge.racToolCallBuildFollowupPrompt( + originalPrompt, + toolsPrompt, + toolName, + toolResultJson, + keepToolsAvailable + ) ?: "" + } + + // ======================================================================== + // JSON NORMALIZATION (NO FALLBACK) + // ======================================================================== + + /** + * Normalize JSON by adding quotes around unquoted keys using C++ implementation. + * + * @param jsonStr Raw JSON string possibly with unquoted keys + * @return Normalized JSON string with all keys quoted + */ + fun normalizeJson(jsonStr: String): String { + return RunAnywhereBridge.racToolCallNormalizeJson(jsonStr) ?: jsonStr + } + + // ======================================================================== + // PRIVATE HELPERS + // ======================================================================== + + /** + * Parse arguments JSON string to Map + */ + private fun parseArgumentsJson(json: String): Map { + return try { + val element = Json.parseToJsonElement(json) + if (element is JsonObject) { + element.mapValues { (_, v) -> jsonElementToToolValue(v) } + } else { + emptyMap() + } + } catch (e: Exception) { + logger.error("Failed to parse arguments JSON: ${e.message}") + emptyMap() + } + } + + /** + * Convert JsonElement to ToolValue + */ + private fun jsonElementToToolValue(element: JsonElement): ToolValue = when (element) { + is JsonPrimitive -> when { + element.isString -> ToolValue.string(element.content) + element.booleanOrNull != null -> ToolValue.bool(element.boolean) + element.doubleOrNull != null -> ToolValue.number(element.double) + else -> ToolValue.string(element.content) + } + is JsonArray -> ToolValue.array(element.map { jsonElementToToolValue(it) }) + is JsonObject -> ToolValue.obj(element.mapValues { (_, v) -> jsonElementToToolValue(v) }) + JsonNull -> ToolValue.nullValue() + } + + /** + * Serialize tool definitions to JSON array string + */ + private fun serializeToolsToJson(tools: List): String { + val jsonArray = buildJsonArray { + tools.forEach { tool -> + addJsonObject { + put("name", tool.name) + put("description", tool.description) + putJsonArray("parameters") { + tool.parameters.forEach { param -> + addJsonObject { + put("name", param.name) + put("type", param.type.value) + put("description", param.description) + put("required", param.required) + param.enumValues?.let { values -> + putJsonArray("enumValues") { + values.forEach { add(it) } + } + } + } + } + } + tool.category?.let { put("category", it) } + } + } + } + return jsonArray.toString() + } + + /** + * Serialize options to JSON string + */ + private fun serializeOptionsToJson(options: ToolCallingOptions): String { + val jsonObj = buildJsonObject { + put("maxToolCalls", options.maxToolCalls) + put("autoExecute", options.autoExecute) + options.temperature?.let { put("temperature", it) } + options.maxTokens?.let { put("maxTokens", it) } + options.systemPrompt?.let { put("systemPrompt", it) } + put("replaceSystemPrompt", options.replaceSystemPrompt) + put("keepToolsAvailable", options.keepToolsAvailable) + put("format", options.format.toFormatName()) // Convert to string at serialization boundary + } + return jsonObj.toString() + } + + /** + * Convert ToolValue to JSON string + */ + fun toolValueToJsonString(value: Map): String { + val jsonObj = buildJsonObject { + value.forEach { (k, v) -> + put(k, toolValueToJsonElement(v)) + } + } + return jsonObj.toString() + } + + private fun toolValueToJsonElement(value: ToolValue): JsonElement = when (value) { + is ToolValue.StringValue -> JsonPrimitive(value.value) + is ToolValue.NumberValue -> JsonPrimitive(value.value) + is ToolValue.BoolValue -> JsonPrimitive(value.value) + is ToolValue.ArrayValue -> buildJsonArray { + value.value.forEach { add(toolValueToJsonElement(it)) } + } + is ToolValue.ObjectValue -> buildJsonObject { + value.value.forEach { (k, v) -> put(k, toolValueToJsonElement(v)) } + } + ToolValue.NullValue -> JsonNull + } +} diff --git a/sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/native/bridge/RunAnywhereBridge.kt b/sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/native/bridge/RunAnywhereBridge.kt index 44820b57e..561ffc0da 100644 --- a/sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/native/bridge/RunAnywhereBridge.kt +++ b/sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/native/bridge/RunAnywhereBridge.kt @@ -855,6 +855,97 @@ object RunAnywhereBridge { baseUrl: String?, ): Int + // ======================================================================== + // TOOL CALLING API (rac_tool_calling.h) + // Mirrors Swift SDK's CppBridge+ToolCalling.swift + // ======================================================================== + + /** + * Parse LLM output for tool calls. + * + * @param llmOutput Raw LLM output text + * @return JSON string with parsed result, or null on error + */ + @JvmStatic + external fun racToolCallParse(llmOutput: String): String? + + /** + * Format tool definitions into system prompt. + * + * @param toolsJson JSON array of tool definitions + * @return Formatted prompt string, or null on error + */ + @JvmStatic + external fun racToolCallFormatPromptJson(toolsJson: String): String? + + /** + * Format tool definitions into system prompt with specified format (int). + * + * @param toolsJson JSON array of tool definitions + * @param format Tool calling format (0=AUTO, 1=DEFAULT, 2=LFM2, 3=OPENAI) + * @return Formatted prompt string, or null on error + */ + @JvmStatic + external fun racToolCallFormatPromptJsonWithFormat(toolsJson: String, format: Int): String? + + /** + * Format tool definitions into system prompt with format specified by name. + * + * *** PREFERRED API - Uses string format name *** + * + * Valid format names (case-insensitive): "auto", "default", "lfm2", "openai" + * C++ is single source of truth for format validation. + * + * @param toolsJson JSON array of tool definitions + * @param formatName Format name string (e.g., "lfm2", "default") + * @return Formatted prompt string, or null on error + */ + @JvmStatic + external fun racToolCallFormatPromptJsonWithFormatName(toolsJson: String, formatName: String): String? + + /** + * Build initial prompt with tools and user query. + * + * @param userPrompt The user's question/request + * @param toolsJson JSON array of tool definitions + * @param optionsJson Options as JSON (nullable) + * @return Complete formatted prompt, or null on error + */ + @JvmStatic + external fun racToolCallBuildInitialPrompt( + userPrompt: String, + toolsJson: String, + optionsJson: String?, + ): String? + + /** + * Build follow-up prompt after tool execution. + * + * @param originalPrompt The original user prompt + * @param toolsPrompt Formatted tools prompt (nullable) + * @param toolName Name of the tool that was executed + * @param toolResultJson JSON string of the tool result + * @param keepToolsAvailable Whether to include tool definitions + * @return Follow-up prompt, or null on error + */ + @JvmStatic + external fun racToolCallBuildFollowupPrompt( + originalPrompt: String, + toolsPrompt: String?, + toolName: String, + toolResultJson: String, + keepToolsAvailable: Boolean, + ): String? + + /** + * Normalize JSON by adding quotes around unquoted keys. + * + * @param jsonStr Raw JSON string possibly with unquoted keys + * @return Normalized JSON string, or null on error + */ + @JvmStatic + external fun racToolCallNormalizeJson(jsonStr: String): String? + // ======================================================================== // CONSTANTS // ======================================================================== diff --git a/sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/LLM/RunAnywhereToolCalling.kt b/sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/LLM/RunAnywhereToolCalling.kt new file mode 100644 index 000000000..89a239d7b --- /dev/null +++ b/sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/LLM/RunAnywhereToolCalling.kt @@ -0,0 +1,351 @@ +/* + * Copyright 2026 RunAnywhere SDK + * SPDX-License-Identifier: Apache-2.0 + * + * Public API for tool calling (function calling) with LLMs. + * Allows LLMs to request external actions (API calls, device functions, etc.) + * + * ARCHITECTURE: + * - CppBridgeToolCalling: C++ bridge for parsing tags (SINGLE SOURCE OF TRUTH) + * - This file: Tool registration, executor storage, orchestration + * - Orchestration: generate โ†’ parse (C++) โ†’ execute โ†’ loop + * + * *** ALL PARSING LOGIC IS IN C++ (rac_tool_calling.h) - NO KOTLIN FALLBACKS *** + * + * Mirrors Swift SDK's RunAnywhere+ToolCalling.swift + */ + +package com.runanywhere.sdk.public.extensions.LLM + +import com.runanywhere.sdk.public.RunAnywhere +import com.runanywhere.sdk.public.extensions.generateStream +import com.runanywhere.sdk.foundation.SDKLogger +import com.runanywhere.sdk.foundation.bridge.extensions.CppBridgeToolCalling +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock + +/** + * Thread-safe tool registry for tool registration and lookup. + */ +private object ToolRegistry { + private val mutex = Mutex() + private val tools = mutableMapOf() + + suspend fun register(definition: ToolDefinition, executor: ToolExecutor) = mutex.withLock { + tools[definition.name] = RegisteredTool(definition, executor) + } + + suspend fun unregister(toolName: String) = mutex.withLock { + tools.remove(toolName) + } + + suspend fun getAll(): List = mutex.withLock { + tools.values.map { it.definition } + } + + suspend fun get(toolName: String): RegisteredTool? = mutex.withLock { + tools[toolName] + } + + suspend fun clear() = mutex.withLock { + tools.clear() + } +} + +/** + * Tool calling extension for RunAnywhere. + */ +object RunAnywhereToolCalling { + private const val TAG = "ToolCalling" + private val logger = SDKLogger(TAG) + + // ======================================================================== + // TOOL REGISTRATION + // ======================================================================== + + /** + * Register a tool that the LLM can use. + * + * Tools are stored in-memory and available for all subsequent generateWithTools calls. + * Executors run in Kotlin and have full access to Kotlin/Android APIs. + * + * @param definition Tool definition (name, description, parameters) + * @param executor Suspend function that executes the tool + */ + suspend fun registerTool( + definition: ToolDefinition, + executor: ToolExecutor + ) { + ToolRegistry.register(definition, executor) + logger.info("Registered tool: ${definition.name}") + } + + /** + * Unregister a tool by name. + * + * @param toolName The name of the tool to remove + */ + suspend fun unregisterTool(toolName: String) { + ToolRegistry.unregister(toolName) + logger.info("Unregistered tool: $toolName") + } + + /** + * Get all registered tool definitions. + * + * @return List of registered tool definitions + */ + suspend fun getRegisteredTools(): List { + return ToolRegistry.getAll() + } + + /** + * Clear all registered tools. + */ + suspend fun clearTools() { + ToolRegistry.clear() + logger.info("Cleared all registered tools") + } + + // ======================================================================== + // TOOL EXECUTION + // ======================================================================== + + /** + * Execute a tool call. + * + * Looks up the tool in the registry and invokes its executor with the provided arguments. + * Returns a ToolResult with success/failure status. + * + * @param toolCall The tool call to execute + * @return Result of the tool execution + */ + suspend fun executeTool(toolCall: ToolCall): ToolResult { + val tool = ToolRegistry.get(toolCall.toolName) + + if (tool == null) { + return ToolResult( + toolName = toolCall.toolName, + success = false, + error = "Unknown tool: ${toolCall.toolName}", + callId = toolCall.callId + ) + } + + return try { + val result = tool.executor(toolCall.arguments) + ToolResult( + toolName = toolCall.toolName, + success = true, + result = result, + callId = toolCall.callId + ) + } catch (e: Exception) { + logger.error("Tool execution failed: ${e.message}") + ToolResult( + toolName = toolCall.toolName, + success = false, + error = e.message ?: "Unknown error", + callId = toolCall.callId + ) + } + } + + // ======================================================================== + // GENERATE WITH TOOLS + // ======================================================================== + + /** + * Generates a response with tool calling support. + * + * Orchestrates a generate โ†’ parse โ†’ execute โ†’ loop cycle: + * 1. Builds a system prompt describing available tools (C++) + * 2. Generates LLM response + * 3. Parses output for `` tags (C++ - SINGLE SOURCE OF TRUTH) + * 4. If tool call found and autoExecute is true, executes and continues + * 5. Repeats until no more tool calls or maxToolCalls reached + * + * @param prompt The user's prompt + * @param options Tool calling options + * @return Result containing final text, all tool calls made, and their results + */ + suspend fun generateWithTools( + prompt: String, + options: ToolCallingOptions? = null + ): ToolCallingResult { + // Ensure SDK is initialized + require(RunAnywhere.isInitialized) { "SDK not initialized" } + + val opts = options ?: ToolCallingOptions() + val registeredTools = ToolRegistry.getAll() + val tools = opts.tools ?: registeredTools + + // Build system prompt using C++ (SINGLE SOURCE OF TRUTH) + val systemPrompt = buildToolSystemPrompt(tools, opts) + var fullPrompt = if (systemPrompt.isEmpty()) prompt else "$systemPrompt\n\nUser: $prompt" + + val allToolCalls = mutableListOf() + val allToolResults = mutableListOf() + var finalText = "" + + repeat(opts.maxToolCalls) { iteration -> + logger.debug("Tool calling iteration $iteration") + + // Generate response + val responseText = generateAndCollect(fullPrompt, opts.temperature, opts.maxTokens) + + // Parse for tool calls using C++ (SINGLE SOURCE OF TRUTH - NO FALLBACK) + val (cleanText, toolCall) = CppBridgeToolCalling.parseToolCallToObject(responseText) + finalText = cleanText + + if (toolCall == null) { + logger.debug("No tool call found, generation complete") + return ToolCallingResult( + text = finalText, + toolCalls = allToolCalls, + toolResults = allToolResults, + isComplete = true + ) + } + + allToolCalls.add(toolCall) + logger.info("Found tool call: ${toolCall.toolName}") + + if (!opts.autoExecute) { + return ToolCallingResult( + text = finalText, + toolCalls = allToolCalls, + toolResults = emptyList(), + isComplete = false + ) + } + + // Execute tool + val result = executeTool(toolCall) + allToolResults.add(result) + logger.info("Tool ${toolCall.toolName} executed: ${if (result.success) "success" else "failed"}") + + // Build follow-up prompt using C++ (SINGLE SOURCE OF TRUTH) + val toolResultJson = CppBridgeToolCalling.toolValueToJsonString( + result.result ?: mapOf("error" to ToolValue.string(result.error ?: "Unknown error")) + ) + + fullPrompt = CppBridgeToolCalling.buildFollowupPrompt( + originalPrompt = prompt, + toolsPrompt = if (opts.keepToolsAvailable) CppBridgeToolCalling.formatToolsForPrompt(tools, opts.format) else null, + toolName = toolCall.toolName, + toolResultJson = toolResultJson, + keepToolsAvailable = opts.keepToolsAvailable + ) + } + + return ToolCallingResult( + text = finalText, + toolCalls = allToolCalls, + toolResults = allToolResults, + isComplete = true + ) + } + + /** + * Continue generation after manual tool execution. + * + * Use this when autoExecute is false. After receiving a ToolCallingResult + * with isComplete: false, execute the tool yourself, then call this to continue. + * + * @param previousPrompt The original user prompt + * @param toolCall The tool call that was executed + * @param toolResult The result of executing the tool + * @param options Tool calling options for the continuation + * @return Result of the continued generation + */ + suspend fun continueWithToolResult( + previousPrompt: String, + toolCall: ToolCall, + toolResult: ToolResult, + options: ToolCallingOptions? = null + ): ToolCallingResult { + val resultJson = CppBridgeToolCalling.toolValueToJsonString( + toolResult.result ?: mapOf("error" to ToolValue.string(toolResult.error ?: "Unknown error")) + ) + + // Build follow-up prompt using C++ (SINGLE SOURCE OF TRUTH) + val tools = options?.tools ?: ToolRegistry.getAll() + val toolsPrompt = if (options?.keepToolsAvailable == true) { + CppBridgeToolCalling.formatToolsForPrompt(tools, options.format) + } else null + + val continuedPrompt = CppBridgeToolCalling.buildFollowupPrompt( + originalPrompt = previousPrompt, + toolsPrompt = toolsPrompt, + toolName = toolCall.toolName, + toolResultJson = resultJson, + keepToolsAvailable = options?.keepToolsAvailable ?: false + ) + + val continuationOptions = ToolCallingOptions( + tools = options?.tools, + maxToolCalls = maxOf(0, (options?.maxToolCalls ?: 5) - 1), + autoExecute = options?.autoExecute ?: true, + temperature = options?.temperature, + maxTokens = options?.maxTokens, + systemPrompt = options?.systemPrompt, + replaceSystemPrompt = options?.replaceSystemPrompt ?: false, + keepToolsAvailable = options?.keepToolsAvailable ?: false, + format = options?.format ?: ToolCallFormat.Default + ) + + return generateWithTools(continuedPrompt, continuationOptions) + } + + // ======================================================================== + // PRIVATE HELPERS + // ======================================================================== + + /** + * Builds the system prompt with tool definitions using C++ implementation. + */ + private fun buildToolSystemPrompt( + tools: List, + options: ToolCallingOptions + ): String { + // Use C++ implementation for prompt formatting (SINGLE SOURCE OF TRUTH) + // Pass the format from options to generate model-specific instructions + val toolsPrompt = CppBridgeToolCalling.formatToolsForPrompt(tools, options.format) + + return when { + options.replaceSystemPrompt && options.systemPrompt != null -> { + options.systemPrompt + } + options.systemPrompt != null -> { + "${options.systemPrompt}\n\n$toolsPrompt" + } + else -> { + toolsPrompt + } + } + } + + /** + * Generate text using streaming and collect all tokens into a single string. + */ + private suspend fun generateAndCollect( + prompt: String, + temperature: Float?, + maxTokens: Int? + ): String { + val genOptions = LLMGenerationOptions( + maxTokens = maxTokens ?: 1024, + temperature = temperature ?: 0.7f + ) + + val tokenFlow = RunAnywhere.generateStream(prompt, genOptions) + + val responseText = StringBuilder() + tokenFlow.collect { token -> + responseText.append(token) + } + + return responseText.toString() + } +} diff --git a/sdk/runanywhere-react-native/.gitignore b/sdk/runanywhere-react-native/.gitignore index 54aa8aa29..89a7142d6 100644 --- a/sdk/runanywhere-react-native/.gitignore +++ b/sdk/runanywhere-react-native/.gitignore @@ -11,6 +11,7 @@ nitrogen/generated/ # iOS ios/build/ **/ios/Binaries/ +**/ios/xcframeworks/ *.xcframework.zip # NOTE: iOS xcframeworks are now bundled in npm package - DO NOT gitignore them diff --git a/sdk/runanywhere-react-native/packages/core/RunAnywhereCore.podspec b/sdk/runanywhere-react-native/packages/core/RunAnywhereCore.podspec index e560c0afd..ed10c3427 100644 --- a/sdk/runanywhere-react-native/packages/core/RunAnywhereCore.podspec +++ b/sdk/runanywhere-react-native/packages/core/RunAnywhereCore.podspec @@ -35,6 +35,7 @@ Pod::Spec.new do |s| "HEADER_SEARCH_PATHS" => [ "$(PODS_TARGET_SRCROOT)/cpp", "$(PODS_TARGET_SRCROOT)/cpp/bridges", + "$(PODS_TARGET_SRCROOT)/cpp/third_party", "$(PODS_TARGET_SRCROOT)/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers", "$(PODS_TARGET_SRCROOT)/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers", "$(PODS_ROOT)/Headers/Public", diff --git a/sdk/runanywhere-react-native/packages/core/android/CMakeLists.txt b/sdk/runanywhere-react-native/packages/core/android/CMakeLists.txt index 6a08dc20c..3456e142c 100644 --- a/sdk/runanywhere-react-native/packages/core/android/CMakeLists.txt +++ b/sdk/runanywhere-react-native/packages/core/android/CMakeLists.txt @@ -5,6 +5,19 @@ set(PACKAGE_NAME runanywherecore) set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_CXX_STANDARD 20) +# ============================================================================= +# nlohmann/json - Header-only JSON library for robust JSON parsing +# Used by ToolCallingBridge for parsing tool call JSON from LLM output +# ============================================================================= +include(FetchContent) +FetchContent_Declare( + nlohmann_json + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG v3.11.3 + GIT_SHALLOW TRUE +) +FetchContent_MakeAvailable(nlohmann_json) + # ============================================================================= # 16KB Page Alignment for Android 15+ (API 35) Compliance # Required starting November 1, 2025 for Google Play submissions @@ -73,6 +86,7 @@ include_directories( "src/main/cpp" "../cpp" "../cpp/bridges" + "../cpp/third_party" "${CMAKE_SOURCE_DIR}/include" # RAC API headers from runanywhere-commons (flat access) "${RAC_INCLUDE_DIR}" @@ -111,6 +125,9 @@ target_link_libraries( target_link_libraries(${PACKAGE_NAME} rac_commons) target_compile_definitions(${PACKAGE_NAME} PRIVATE HAS_RACOMMONS=1) +# Link nlohmann_json for ToolCallingBridge +target_link_libraries(${PACKAGE_NAME} nlohmann_json::nlohmann_json) + # 16KB page alignment - MUST be on target for Android 15+ compliance target_link_options(${PACKAGE_NAME} PRIVATE -Wl,-z,max-page-size=16384) diff --git a/sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.cpp b/sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.cpp index dbb75ae09..22882e14e 100644 --- a/sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.cpp +++ b/sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.cpp @@ -35,6 +35,7 @@ #include "bridges/HTTPBridge.hpp" #include "bridges/DownloadBridge.hpp" #include "bridges/TelemetryBridge.hpp" +#include "bridges/ToolCallingBridge.hpp" // RACommons C API headers for capability methods // These are backend-agnostic - they work with any registered backend @@ -2569,4 +2570,71 @@ std::shared_ptr> HybridRunAnywhereCore::isTelemetryInitialized() { }); } +// ============================================================================ +// Tool Calling +// +// ARCHITECTURE: +// - C++ (ToolCallingBridge): Parses tags from LLM output. +// This is the SINGLE SOURCE OF TRUTH for parsing, ensuring consistency. +// +// - TypeScript (RunAnywhere+ToolCalling.ts): Handles tool registry, executor +// storage, prompt formatting, and orchestration. Executors MUST stay in +// TypeScript because they need JavaScript APIs (fetch, device APIs, etc.). +// +// Only parseToolCallFromOutput is implemented in C++. All other tool calling +// functionality (registration, execution, prompt formatting) is in TypeScript. +// ============================================================================ + +std::shared_ptr> HybridRunAnywhereCore::parseToolCallFromOutput(const std::string& llmOutput) { + return Promise::async([llmOutput]() -> std::string { + LOGD("parseToolCallFromOutput: input length=%zu", llmOutput.length()); + + // Use ToolCallingBridge for parsing - single source of truth + // This ensures consistent tag parsing across all platforms + return ::runanywhere::bridges::ToolCallingBridge::shared().parseToolCall(llmOutput); + }); +} + +std::shared_ptr> HybridRunAnywhereCore::formatToolsForPrompt( + const std::string& toolsJson, + const std::string& format +) { + return Promise::async([toolsJson, format]() -> std::string { + LOGD("formatToolsForPrompt: tools length=%zu, format=%s", toolsJson.length(), format.c_str()); + + // Use C++ single source of truth for prompt formatting + // This eliminates duplicate TypeScript implementation + return ::runanywhere::bridges::ToolCallingBridge::shared().formatToolsPrompt(toolsJson, format); + }); +} + +std::shared_ptr> HybridRunAnywhereCore::buildInitialPrompt( + const std::string& userPrompt, + const std::string& toolsJson, + const std::string& optionsJson +) { + return Promise::async([userPrompt, toolsJson, optionsJson]() -> std::string { + LOGD("buildInitialPrompt: prompt length=%zu, tools length=%zu", userPrompt.length(), toolsJson.length()); + + // Use C++ single source of truth for initial prompt building + return ::runanywhere::bridges::ToolCallingBridge::shared().buildInitialPrompt(userPrompt, toolsJson, optionsJson); + }); +} + +std::shared_ptr> HybridRunAnywhereCore::buildFollowupPrompt( + const std::string& originalPrompt, + const std::string& toolsPrompt, + const std::string& toolName, + const std::string& resultJson, + bool keepToolsAvailable +) { + return Promise::async([originalPrompt, toolsPrompt, toolName, resultJson, keepToolsAvailable]() -> std::string { + LOGD("buildFollowupPrompt: tool=%s, keepTools=%d", toolName.c_str(), keepToolsAvailable); + + // Use C++ single source of truth for follow-up prompt building + return ::runanywhere::bridges::ToolCallingBridge::shared().buildFollowupPrompt( + originalPrompt, toolsPrompt, toolName, resultJson, keepToolsAvailable); + }); +} + } // namespace margelo::nitro::runanywhere diff --git a/sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.hpp b/sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.hpp index 5291493e1..8d17aa6b9 100644 --- a/sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.hpp +++ b/sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.hpp @@ -257,6 +257,17 @@ class HybridRunAnywhereCore : public HybridRunAnywhereCoreSpec { std::shared_ptr> voiceAgentSynthesizeSpeech(const std::string& text) override; std::shared_ptr> cleanupVoiceAgent() override; + // ============================================================================ + // Tool Calling - Delegates to ToolCallingBridge + // Single source of truth for parsing, formatting, and prompt building + // Tool registry and execution are in TypeScript (RunAnywhere+ToolCalling.ts) + // ============================================================================ + + std::shared_ptr> parseToolCallFromOutput(const std::string& llmOutput) override; + std::shared_ptr> formatToolsForPrompt(const std::string& toolsJson, const std::string& format) override; + std::shared_ptr> buildInitialPrompt(const std::string& userPrompt, const std::string& toolsJson, const std::string& optionsJson) override; + std::shared_ptr> buildFollowupPrompt(const std::string& originalPrompt, const std::string& toolsPrompt, const std::string& toolName, const std::string& resultJson, bool keepToolsAvailable) override; + private: // Thread safety std::mutex initMutex_; diff --git a/sdk/runanywhere-react-native/packages/core/cpp/bridges/ToolCallingBridge.cpp b/sdk/runanywhere-react-native/packages/core/cpp/bridges/ToolCallingBridge.cpp new file mode 100644 index 000000000..873b28b2c --- /dev/null +++ b/sdk/runanywhere-react-native/packages/core/cpp/bridges/ToolCallingBridge.cpp @@ -0,0 +1,188 @@ +/** + * @file ToolCallingBridge.cpp + * @brief Tool Calling bridge implementation - THIN WRAPPER + * + * *** ALL LOGIC IS IN runanywhere-commons (rac_tool_calling.h) *** + * + * This bridge just wraps the C API functions from commons. + * NO LOCAL PARSING LOGIC - everything calls through to commons. + */ + +#include "ToolCallingBridge.hpp" +#include +#include +#include + +using json = nlohmann::json; + +namespace runanywhere { +namespace bridges { + +ToolCallingBridge& ToolCallingBridge::shared() { + static ToolCallingBridge instance; + return instance; +} + +std::string ToolCallingBridge::parseToolCall(const std::string& llmOutput) { + rac_tool_call_t result = {}; // Zero-initialize for safety + rac_result_t rc = rac_tool_call_parse(llmOutput.c_str(), &result); + + // Handle parse failure defensively - return safe default + if (rc != RAC_SUCCESS) { + json fallback; + fallback["hasToolCall"] = false; + fallback["cleanText"] = llmOutput; + return fallback.dump(); + } + + // Build JSON response using nlohmann/json + json response; + response["hasToolCall"] = result.has_tool_call == RAC_TRUE; + response["cleanText"] = result.clean_text ? result.clean_text : llmOutput; + + if (result.has_tool_call == RAC_TRUE) { + response["toolName"] = result.tool_name ? result.tool_name : ""; + + if (result.arguments_json) { + try { + response["argumentsJson"] = json::parse(result.arguments_json); + } catch (...) { + response["argumentsJson"] = json::object(); + } + } else { + response["argumentsJson"] = json::object(); + } + response["callId"] = result.call_id; + } + + rac_tool_call_free(&result); + return response.dump(); +} + +std::string ToolCallingBridge::formatToolsPrompt(const std::string& toolsJson, const std::string& format) { + if (toolsJson.empty() || toolsJson == "[]") { + return ""; + } + + char* prompt = nullptr; + rac_result_t rc = rac_tool_call_format_prompt_json_with_format_name( + toolsJson.c_str(), + format.c_str(), + &prompt + ); + + if (rc != RAC_SUCCESS || !prompt) { + return ""; + } + + std::string result(prompt); + rac_free(prompt); + return result; +} + +std::string ToolCallingBridge::buildInitialPrompt( + const std::string& userPrompt, + const std::string& toolsJson, + const std::string& optionsJson +) { + // Start with default options + rac_tool_calling_options_t options = { + 5, // max_tool_calls + RAC_TRUE, // auto_execute + 0.7f, // temperature + 1024, // max_tokens + nullptr, // system_prompt + RAC_FALSE, // replace_system_prompt + RAC_FALSE, // keep_tools_available + RAC_TOOL_FORMAT_DEFAULT // format + }; + + // Parse optionsJson if provided + if (!optionsJson.empty()) { + try { + json opts = json::parse(optionsJson); + + if (opts.contains("maxToolCalls") && opts["maxToolCalls"].is_number_integer()) { + options.max_tool_calls = opts["maxToolCalls"].get(); + } + if (opts.contains("autoExecute") && opts["autoExecute"].is_boolean()) { + options.auto_execute = opts["autoExecute"].get() ? RAC_TRUE : RAC_FALSE; + } + if (opts.contains("temperature") && opts["temperature"].is_number()) { + options.temperature = opts["temperature"].get(); + } + if (opts.contains("maxTokens") && opts["maxTokens"].is_number_integer()) { + options.max_tokens = opts["maxTokens"].get(); + } + if (opts.contains("format") && opts["format"].is_string()) { + options.format = rac_tool_call_format_from_name(opts["format"].get().c_str()); + } + if (opts.contains("replaceSystemPrompt") && opts["replaceSystemPrompt"].is_boolean()) { + options.replace_system_prompt = opts["replaceSystemPrompt"].get() ? RAC_TRUE : RAC_FALSE; + } + if (opts.contains("keepToolsAvailable") && opts["keepToolsAvailable"].is_boolean()) { + options.keep_tools_available = opts["keepToolsAvailable"].get() ? RAC_TRUE : RAC_FALSE; + } + } catch (...) { + // JSON parse failed, keep defaults + } + } + + char* prompt = nullptr; + rac_result_t rc = rac_tool_call_build_initial_prompt( + userPrompt.c_str(), + toolsJson.c_str(), + &options, + &prompt + ); + + if (rc != RAC_SUCCESS || !prompt) { + return userPrompt; + } + + std::string result(prompt); + rac_free(prompt); + return result; +} + +std::string ToolCallingBridge::buildFollowupPrompt( + const std::string& originalPrompt, + const std::string& toolsPrompt, + const std::string& toolName, + const std::string& resultJson, + bool keepToolsAvailable +) { + char* prompt = nullptr; + rac_result_t rc = rac_tool_call_build_followup_prompt( + originalPrompt.c_str(), + toolsPrompt.empty() ? nullptr : toolsPrompt.c_str(), + toolName.c_str(), + resultJson.c_str(), + keepToolsAvailable ? RAC_TRUE : RAC_FALSE, + &prompt + ); + + if (rc != RAC_SUCCESS || !prompt) { + return ""; + } + + std::string result(prompt); + rac_free(prompt); + return result; +} + +std::string ToolCallingBridge::normalizeJson(const std::string& jsonStr) { + char* normalized = nullptr; + rac_result_t rc = rac_tool_call_normalize_json(jsonStr.c_str(), &normalized); + + if (rc != RAC_SUCCESS || !normalized) { + return jsonStr; + } + + std::string result(normalized); + rac_free(normalized); + return result; +} + +} // namespace bridges +} // namespace runanywhere diff --git a/sdk/runanywhere-react-native/packages/core/cpp/bridges/ToolCallingBridge.hpp b/sdk/runanywhere-react-native/packages/core/cpp/bridges/ToolCallingBridge.hpp new file mode 100644 index 000000000..8a4ff4579 --- /dev/null +++ b/sdk/runanywhere-react-native/packages/core/cpp/bridges/ToolCallingBridge.hpp @@ -0,0 +1,98 @@ +/** + * @file ToolCallingBridge.hpp + * @brief Tool Calling bridge for React Native - THIN WRAPPER + * + * *** SINGLE SOURCE OF TRUTH FOR TOOL CALLING LOGIC IS IN COMMONS C++ *** + * + * All parsing logic is in runanywhere-commons (rac_tool_calling.h). + * This bridge just wraps the C API for JSI access. + * + * ARCHITECTURE: + * - Commons C++: ALL parsing, prompt formatting, JSON normalization + * - This bridge: Thin wrapper that calls rac_tool_call_* functions + * - TypeScript: Tool registry, execution (needs JS APIs) + */ + +#pragma once + +#include + +namespace runanywhere { +namespace bridges { + +/** + * Tool Calling bridge - thin wrapper around commons API + * + * *** NO LOCAL PARSING LOGIC - ALL CALLS GO TO COMMONS *** + */ +class ToolCallingBridge { +public: + static ToolCallingBridge& shared(); + + /** + * Format tool definitions into a system prompt. + * Calls rac_tool_call_format_prompt_json_with_format_name() from commons. + * + * @param toolsJson JSON array of tool definitions + * @param format Format name ("default" or "lfm2") + * @return Formatted system prompt string + */ + std::string formatToolsPrompt(const std::string& toolsJson, const std::string& format = "default"); + + /** + * Parse LLM output for tool calls. + * Calls rac_tool_call_parse() from commons. + * + * @param llmOutput Raw LLM output text + * @return JSON string with hasToolCall, toolName, argumentsJson, cleanText + */ + std::string parseToolCall(const std::string& llmOutput); + + /** + * Build initial prompt with tools and user query. + * Calls rac_tool_call_build_initial_prompt() from commons. + * + * @param userPrompt User's question/request + * @param toolsJson JSON array of tool definitions + * @param optionsJson Options as JSON (nullable) + * @return Complete formatted prompt + */ + std::string buildInitialPrompt(const std::string& userPrompt, + const std::string& toolsJson, + const std::string& optionsJson); + + /** + * Build follow-up prompt after tool execution. + * Calls rac_tool_call_build_followup_prompt() from commons. + * + * @param originalPrompt Original user prompt + * @param toolsPrompt Formatted tools prompt (can be empty) + * @param toolName Name of executed tool + * @param resultJson Tool result as JSON + * @param keepToolsAvailable Whether to keep tools in follow-up + * @return Follow-up prompt string + */ + std::string buildFollowupPrompt(const std::string& originalPrompt, + const std::string& toolsPrompt, + const std::string& toolName, + const std::string& resultJson, + bool keepToolsAvailable); + + /** + * Normalize JSON by adding quotes around unquoted keys. + * Calls rac_tool_call_normalize_json() from commons. + * + * @param jsonStr Raw JSON possibly with unquoted keys + * @return Normalized JSON string + */ + std::string normalizeJson(const std::string& jsonStr); + +private: + ToolCallingBridge() = default; + ~ToolCallingBridge() = default; + ToolCallingBridge(const ToolCallingBridge&) = delete; + ToolCallingBridge& operator=(const ToolCallingBridge&) = delete; +}; + +} // namespace bridges +} // namespace runanywhere diff --git a/sdk/runanywhere-react-native/packages/core/cpp/third_party/nlohmann/json.hpp b/sdk/runanywhere-react-native/packages/core/cpp/third_party/nlohmann/json.hpp new file mode 100644 index 000000000..8b72ea653 --- /dev/null +++ b/sdk/runanywhere-react-native/packages/core/cpp/third_party/nlohmann/json.hpp @@ -0,0 +1,24765 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file docs/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // nullptr_t +#include // exception +#if JSON_DIAGNOSTICS + #include // accumulate +#endif +#include // runtime_error +#include // to_string +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // declval, pair +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +// https://en.cppreference.com/w/cpp/experimental/is_detected +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + + +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson +// SPDX-License-Identifier: MIT + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions (except those affecting ABI) +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// #include + + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#ifdef __has_include + #if __has_include() + #include + #endif +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1914 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ + && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + +#ifndef JSON_HAS_RANGES + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 + #define JSON_HAS_RANGES 0 + #elif defined(__cpp_lib_ranges) + #define JSON_HAS_RANGES 1 + #else + #define JSON_HAS_RANGES 0 + #endif +#endif + +#ifndef JSON_HAS_STATIC_RTTI + #if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0 + #define JSON_HAS_STATIC_RTTI 1 + #else + #define JSON_HAS_STATIC_RTTI 0 + #endif +#endif + +#ifdef JSON_HAS_CPP_17 + #define JSON_INLINE_VARIABLE inline +#else + #define JSON_INLINE_VARIABLE +#endif + +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType, \ + class CustomBaseClass> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DISABLE_ENUM_SERIALIZATION + #define JSON_DISABLE_ENUM_SERIALIZATION 0 +#endif + +#ifndef JSON_USE_GLOBAL_UDLS + #define JSON_USE_GLOBAL_UDLS 1 +#endif + +#if JSON_HAS_THREE_WAY_COMPARISON + #include // partial_ordering +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +#if JSON_HAS_THREE_WAY_COMPARISON + inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* +#else + inline bool operator<(const value_t lhs, const value_t rhs) noexcept +#endif +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); +#if JSON_HAS_THREE_WAY_COMPARISON + if (l_index < order.size() && r_index < order.size()) + { + return order[l_index] <=> order[r_index]; // *NOPAD* + } + return std::partial_ordering::unordered; +#else + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +#endif +} + +// GCC selects the built-in operator< over an operator rewritten from +// a user-defined spaceship operator +// Clang, MSVC, and ICC select the rewritten candidate +// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) +#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + return std::is_lt(lhs <=> rhs); // *NOPAD* +} +#endif + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != StringType::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +template +inline StringType escape(StringType s) +{ + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +template +static void unescape(StringType& s) +{ + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // size_t + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2018 The Abseil Authors +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static JSON_INLINE_VARIABLE constexpr T value{}; +}; + +#ifndef JSON_HAS_CPP_17 + template + constexpr T static_const::value; +#endif + +template +inline constexpr std::array make_array(Args&& ... args) +{ + return std::array {{static_cast(std::forward(args))...}}; +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple +#include // char_traits + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // random_access_iterator_tag + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); + +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); + +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ + #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + #include // int64_t, uint64_t + #include // map + #include // allocator + #include // string + #include // vector + + // #include + + + /*! + @brief namespace for Niels Lohmann + @see https://github.com/nlohmann + @since version 1.0.0 + */ + NLOHMANN_JSON_NAMESPACE_BEGIN + + /*! + @brief default JSONSerializer template argument + + This serializer ignores the template arguments and uses ADL + ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) + for serialization. + */ + template + struct adl_serializer; + + /// a class to store JSON values + /// @sa https://json.nlohmann.me/api/basic_json/ + template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector, // cppcheck-suppress syntaxError + class CustomBaseClass = void> + class basic_json; + + /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document + /// @sa https://json.nlohmann.me/api/json_pointer/ + template + class json_pointer; + + /*! + @brief default specialization + @sa https://json.nlohmann.me/api/json/ + */ + using json = basic_json<>; + + /// @brief a minimal map-like container that preserves insertion order + /// @sa https://json.nlohmann.me/api/ordered_map/ + template + struct ordered_map; + + /// @brief specialization that maintains the insertion order of object keys + /// @sa https://json.nlohmann.me/api/ordered_json/ + using ordered_json = basic_json; + + NLOHMANN_JSON_NAMESPACE_END + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +NLOHMANN_JSON_NAMESPACE_BEGIN +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ + +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +// used by exceptions create() member functions +// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; + +///////////////// +// char_traits // +///////////////// + +// Primary template of char_traits calls std char_traits +template +struct char_traits : std::char_traits +{}; + +// Explicitly define char traits for unsigned char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = unsigned char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(EOF); + } +}; + +// Explicitly define char traits for signed char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = signed char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(EOF); + } +}; + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B { }; +template +struct conjunction +: std::conditional(B::value), conjunction, B>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type +{ + // launder type through decltype() to fix compilation failure on ICPC +#ifdef __INTEL_COMPILER + using laundered_type = decltype(std::declval()); +#else + using laundered_type = ConstructibleStringType; +#endif + + static constexpr auto value = + conjunction < + is_constructible, + is_detected_exact>::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected::value&& + is_iterator_traits>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same>::value >> +{ + static constexpr bool value = + is_constructible>::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same>::value&& + is_complete_type < + detected_t>::value >> +{ + using value_type = range_value_t; + + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template