Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ let onnxRuntimeMacOSPath = "\(packageDir)/sdk/runanywhere-swift/Binaries/onnxrun
// ./scripts/build-swift.sh --set-remote (sets useLocalBinaries = false)
//
// =============================================================================
let useLocalBinaries = false // Toggle: true for local dev, false for release
let useLocalBinaries = false // Toggle: true for local dev, false for release

// Version for remote XCFrameworks (used when testLocal = false)
// Updated automatically by CI/CD during releases
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {

private var generationJob: Job? = null

private val generationPrefs by lazy {
getApplication<Application>().getSharedPreferences("generation_settings", android.content.Context.MODE_PRIVATE)
}
Comment on lines +83 to +85
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for all occurrences of "generation_settings" and related SharedPreferences usage
echo "=== Searching for 'generation_settings' string ==="
rg -n "generation_settings" --type=kt

echo -e "\n=== Searching for SharedPreferences writes in SettingsViewModel ==="
fd -t f "SettingsViewModel.kt" --exec rg -l "SharedPreferences\|putFloat\|putInt\|putString" {} \;

echo -e "\n=== Checking if SettingsViewModel exists and what SharedPreferences keys it uses ==="
fd -t f "SettingsViewModel.kt" -x cat -n {}

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 46453


🏁 Script executed:

#!/bin/bash
# Find ChatViewModel.kt
echo "=== Finding ChatViewModel.kt ==="
fd "ChatViewModel" --type f

echo -e "\n=== Reading ChatViewModel.kt around lines 83-85 ==="
fd "ChatViewModel.kt" --type f -x cat -n {} | head -100

echo -e "\n=== Search for all uses of 'generation_settings' with correct syntax ==="
rg "generation_settings" -t kotlin --no-heading

echo -e "\n=== Check if ChatViewModel imports or uses SettingsViewModel constants ==="
fd "ChatViewModel.kt" --type f -x rg "PREFS_NAME\|generation_settings" -n {}

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 5890


🏁 Script executed:

#!/bin/bash
# Find what ChatViewModel does with generationPrefs - look for getGenerationOptions
echo "=== Find getGenerationOptions method in ChatViewModel ==="
fd "ChatViewModel.kt" --type f -x sed -n '850,900p' {}

echo -e "\n=== Check if ChatViewModel could use SettingsViewModel.getGenerationSettings() ==="
fd "ChatViewModel.kt" --type f -x grep -n "getGenerationSettings\|generationPrefs" {}

echo -e "\n=== Check full context of generation prefs usage ==="
fd "ChatViewModel.kt" --type f -x sed -n '83,400p' {} | head -200

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 10463


Extract SharedPreferences file name to a shared constant.

The preference file name "generation_settings" is hardcoded as a string literal. If the name defined in SettingsViewModel changes, ChatViewModel will silently use stale preferences. Extract this into a constants file (e.g., PreferencesConstants.kt) and import it in both view models, or make SettingsViewModel.PREFS_NAME public so ChatViewModel can reference it directly.

🤖 Prompt for AI Agents
In
`@examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatViewModel.kt`
around lines 83 - 85, Replace the hardcoded preference file name
"generation_settings" used when initializing generationPrefs with a shared
constant so both view models reference the same identifier; either create a
PreferencesConstants.kt with a public val (e.g., PREFS_NAME) and import it into
ChatViewModel (used in generationPrefs) and SettingsViewModel, or make
SettingsViewModel.PREFS_NAME public and reference that constant from
ChatViewModel.getSharedPreferences to avoid drift.


init {
// Always start with a new conversation for a fresh chat experience
val conversation = conversationStore.createConversation()
Expand Down Expand Up @@ -342,7 +346,7 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {

try {
// Use SDK streaming generation - returns Flow<String>
RunAnywhere.generateStream(prompt).collect { token ->
RunAnywhere.generateStream(prompt, getGenerationOptions()).collect { token ->
fullResponse += token
totalTokensReceived++

Expand Down Expand Up @@ -466,7 +470,7 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {

try {
// RunAnywhere.generate() returns LLMGenerationResult
val result = RunAnywhere.generate(prompt)
val result = RunAnywhere.generate(prompt, getGenerationOptions())
val response = result.text
val endTime = System.currentTimeMillis()

Expand Down Expand Up @@ -846,6 +850,25 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
_uiState.value = _uiState.value.copy(error = null)
}

/**
* Get generation options from SharedPreferences
*/
private fun getGenerationOptions(): com.runanywhere.sdk.public.extensions.LLM.LLMGenerationOptions {
val temperature = generationPrefs.getFloat("defaultTemperature", 0.7f)
val maxTokens = generationPrefs.getInt("defaultMaxTokens", 1000)
val systemPromptValue = generationPrefs.getString("defaultSystemPrompt", "")
val systemPrompt = if (systemPromptValue.isNullOrEmpty()) null else systemPromptValue
val systemPromptInfo = systemPrompt?.let { "set(${it.length} chars)" } ?: "nil"

Log.i(TAG, "[PARAMS] App getGenerationOptions: temperature=$temperature, maxTokens=$maxTokens, systemPrompt=$systemPromptInfo")

return com.runanywhere.sdk.public.extensions.LLM.LLMGenerationOptions(
maxTokens = maxTokens,
temperature = temperature,
systemPrompt = systemPrompt
)
}

/**
* Format a ToolValue map to JSON string for display.
* Uses kotlinx.serialization for proper JSON escaping of special characters.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package com.runanywhere.runanywhereai.presentation.settings

import android.app.Application
import android.content.Intent
import android.net.Uri
import android.text.format.Formatter
Expand Down Expand Up @@ -31,13 +32,12 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.runanywhere.runanywhereai.ui.theme.AppColors
import com.runanywhere.runanywhereai.ui.theme.AppTypography
import com.runanywhere.runanywhereai.ui.theme.Dimensions
import android.app.Application

/**
* Settings screen
* Settings & Storage Screen
*
* Section order: Generation Settings, API Configuration, Storage Overview, Downloaded Models,
* Storage Management, Logging Configuration, About.
* Section order: API Configuration, Generation Settings, Tool Calling,
* Storage Overview, Downloaded Models, Storage Management, Logging Configuration, About.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand Down Expand Up @@ -72,54 +72,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = viewModel()) {
)
}

// 1. Generation Settings
SettingsSection(title = "Generation Settings") {
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text(
text = "Temperature: ${"%.2f".format(uiState.temperature)}",
style = AppTypography.caption,
color = AppColors.textSecondary,
)
Slider(
value = uiState.temperature,
onValueChange = { viewModel.updateTemperature(it) },
valueRange = 0f..2f,
steps = 19,
modifier = Modifier.fillMaxWidth(),
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "Max Tokens: ${uiState.maxTokens}",
style = MaterialTheme.typography.bodyMedium,
)
Row(verticalAlignment = Alignment.CenterVertically) {
OutlinedButton(
onClick = { viewModel.updateMaxTokens((uiState.maxTokens - 500).coerceAtLeast(500)) },
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp),
modifier = Modifier.height(32.dp),
) { Text("-", style = AppTypography.caption) }
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "${uiState.maxTokens}",
style = AppTypography.caption,
modifier = Modifier.widthIn(min = 48.dp),
)
Spacer(modifier = Modifier.width(8.dp))
OutlinedButton(
onClick = { viewModel.updateMaxTokens((uiState.maxTokens + 500).coerceAtMost(20000)) },
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp),
modifier = Modifier.height(32.dp),
) { Text("+", style = AppTypography.caption) }
}
}
}
}

// 2. API Configuration (Testing)
// 1. API Configuration (Testing)
SettingsSection(title = "API Configuration (Testing)") {
Row(
modifier = Modifier
Expand Down Expand Up @@ -182,10 +135,99 @@ fun SettingsScreen(viewModel: SettingsViewModel = viewModel()) {
)
}

// Tool Calling Section
// 2. Generation Settings Section
SettingsSection(title = "Generation Settings") {
// Temperature Slider
Column(modifier = Modifier.fillMaxWidth()) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "Temperature",
style = MaterialTheme.typography.bodyLarge,
)
Text(
text = String.format("%.1f", uiState.temperature),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
Slider(
value = uiState.temperature,
onValueChange = { viewModel.updateTemperature(it) },
valueRange = 0f..2f,
steps = 19, // 0.1 increments from 0.0 to 2.0
modifier = Modifier.fillMaxWidth(),
)
}

HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))

// Max Tokens Slider
Column(modifier = Modifier.fillMaxWidth()) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "Max Tokens",
style = MaterialTheme.typography.bodyLarge,
)
Text(
text = uiState.maxTokens.toString(),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
Slider(
value = uiState.maxTokens.toFloat(),
onValueChange = { viewModel.updateMaxTokens(it.toInt()) },
valueRange = 50f..4096f,
steps = 80, // 50-token increments
modifier = Modifier.fillMaxWidth(),
)
Comment on lines +185 to +191
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Max Tokens slider steps don't produce clean 50-token increments.

With valueRange = 50f..4096f and steps = 80, the Slider creates 81 intervals of size (4096 - 50) / 81 ≈ 49.95, so the snap points won't land on round numbers. Users will see values like 150, 250, 349, 449… after toInt().

If clean 50-token increments are desired, consider changing the range to 50f..4050f with steps = 79, or use a different max that's reachable in exact 50-step increments from 50.

Suggested fix (clean 50-token steps)
                 Slider(
                     value = uiState.maxTokens.toFloat(),
                     onValueChange = { viewModel.updateMaxTokens(it.toInt()) },
-                    valueRange = 50f..4096f,
-                    steps = 80, // 50-token increments
+                    valueRange = 50f..4050f,
+                    steps = 79, // 50-token increments: 50, 100, 150, ..., 4050
                     modifier = Modifier.fillMaxWidth(),
                 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Slider(
value = uiState.maxTokens.toFloat(),
onValueChange = { viewModel.updateMaxTokens(it.toInt()) },
valueRange = 50f..4096f,
steps = 80, // 50-token increments
modifier = Modifier.fillMaxWidth(),
)
Slider(
value = uiState.maxTokens.toFloat(),
onValueChange = { viewModel.updateMaxTokens(it.toInt()) },
valueRange = 50f..4050f,
steps = 79, // 50-token increments: 50, 100, 150, ..., 4050
modifier = Modifier.fillMaxWidth(),
)
🤖 Prompt for AI Agents
In
`@examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/settings/SettingsScreen.kt`
around lines 185 - 191, The Slider configuration for max tokens (the Slider
composable using uiState.maxTokens and viewModel.updateMaxTokens) currently uses
valueRange = 50f..4096f and steps = 80 which yields non‑integer 50-token
increments; adjust the range and steps so the interval divides evenly (for
example change valueRange to 50f..4050f and steps to 79 or pick a max that is 50
* N + 50 from 50) so the slider snap points map to exact 50-token multiples and
onValueChange.toInt() produces clean values.

}

HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))

// System Prompt TextField
OutlinedTextField(
value = uiState.systemPrompt,
onValueChange = { viewModel.updateSystemPrompt(it) },
label = { Text("System Prompt") },
placeholder = { Text("Enter system prompt (optional)") },
modifier = Modifier.fillMaxWidth(),
maxLines = 3,
textStyle = MaterialTheme.typography.bodyMedium,
)

Spacer(modifier = Modifier.height(8.dp))

// Save Button
OutlinedButton(
onClick = { viewModel.saveGenerationSettings() },
colors = ButtonDefaults.outlinedButtonColors(
contentColor = AppColors.primaryAccent,
),
) {
Text("Save Settings")
}

Spacer(modifier = Modifier.height(8.dp))
Text(
text = "These settings affect LLM text generation.",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}

// 3. Tool Calling Section
ToolSettingsSection()

// 3. Storage Overview - iOS Label(systemImage: "externaldrive") etc.
// 4. Storage Overview
SettingsSection(
title = "Storage Overview",
trailing = {
Expand Down Expand Up @@ -223,7 +265,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = viewModel()) {
)
}

// 4. Downloaded Models
// 5. Downloaded Models
SettingsSection(title = "Downloaded Models") {
if (uiState.downloadedModels.isEmpty()) {
Text(
Expand All @@ -245,7 +287,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = viewModel()) {
}
}

// 5. Storage Management - iOS trash icon, red/orange
// 6. Storage Management
SettingsSection(title = "Storage Management") {
StorageManagementButton(
title = "Clear Cache",
Expand All @@ -264,7 +306,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = viewModel()) {
)
}

// 6. Logging Configuration - iOS Toggle "Log Analytics Locally"
// 7. Logging Configuration
SettingsSection(title = "Logging Configuration") {
Row(
modifier = Modifier.fillMaxWidth(),
Expand All @@ -288,7 +330,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = viewModel()) {
)
}

// 7. About - iOS Label "RunAnywhere SDK" systemImage "cube", "Documentation" systemImage "book"
// 8. About
SettingsSection(title = "About") {
Row(
modifier = Modifier.padding(vertical = 8.dp),
Expand Down Expand Up @@ -394,7 +436,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = viewModel()) {
)
}

// Restart Required Dialog - iOS exact message
// Restart Required Dialog
if (uiState.showRestartDialog) {
AlertDialog(
onDismissRequest = { viewModel.dismissRestartDialog() },
Expand Down Expand Up @@ -712,8 +754,7 @@ private fun ApiConfigurationDialog(

/**
* Tool Calling Settings Section
*
* Allows users to:
* * Allows users to:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor KDoc typo: extra *.

Line 757 reads * * Allows users to: — the leading * is duplicated.

- * * Allows users to:
+ * Allows users to:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* * Allows users to:
* Allows users to:
🤖 Prompt for AI Agents
In
`@examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/settings/SettingsScreen.kt`
at line 757, In the KDoc for SettingsScreen (SettingsScreen.kt) there is a
duplicated asterisk in the list description ("* * Allows users to:"); edit the
KDoc comment so the line reads a single leading list marker ("* Allows users
to:") to remove the extra `* ` and keep the list formatting consistent.

* - Enable/disable tool calling
* - Register demo tools (weather, time, calculator)
* - Clear all registered tools
Expand Down Expand Up @@ -859,4 +900,4 @@ fun ToolSettingsSection() {
)
}
}
}
}
Loading
Loading