Skip to content

Commit 9ccc748

Browse files
minor fixes
1 parent 657fa34 commit 9ccc748

4 files changed

Lines changed: 44 additions & 16 deletions

File tree

examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/lora/LoraAdapterPickerSheet.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ private fun CatalogAdapterRow(
223223
onApply: (Float) -> Unit,
224224
onRemove: () -> Unit,
225225
) {
226-
var scale by remember(entry.id) { mutableFloatStateOf(entry.defaultScale) }
226+
var scale by remember(entry.id, entry.defaultScale) { mutableFloatStateOf(entry.defaultScale) }
227227

228228
Column(
229229
modifier = Modifier

examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/lora/LoraViewModel.kt

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ data class LoraUiState(
3131
val registeredAdapters: List<LoraAdapterCatalogEntry> = emptyList(),
3232
val loadedAdapters: List<LoRAAdapterInfo> = emptyList(),
3333
val compatibleAdapters: List<LoraAdapterCatalogEntry> = emptyList(),
34+
val downloadedAdapterPaths: Map<String, String> = emptyMap(),
3435
val downloadingAdapterId: String? = null,
3536
val downloadProgress: Float = 0f,
3637
val error: String? = null,
@@ -58,13 +59,15 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
5859
fun refresh() {
5960
viewModelScope.launch {
6061
try {
61-
val (registered, loaded) = withContext(Dispatchers.IO) {
62-
RunAnywhere.allRegisteredLoraAdapters() to RunAnywhere.getLoadedLoraAdapters()
62+
val (registered, loaded, downloaded) = withContext(Dispatchers.IO) {
63+
val reg = RunAnywhere.allRegisteredLoraAdapters()
64+
Triple(reg, RunAnywhere.getLoadedLoraAdapters(), scanDownloadedAdapters(reg))
6365
}
6466
_uiState.update {
6567
it.copy(
6668
registeredAdapters = registered,
6769
loadedAdapters = loaded,
70+
downloadedAdapterPaths = downloaded,
6871
error = null,
6972
)
7073
}
@@ -79,13 +82,15 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
7982
fun refreshForModel(modelId: String) {
8083
viewModelScope.launch {
8184
try {
82-
val (compatible, loaded) = withContext(Dispatchers.IO) {
83-
RunAnywhere.loraAdaptersForModel(modelId) to RunAnywhere.getLoadedLoraAdapters()
85+
val (compatible, loaded, downloaded) = withContext(Dispatchers.IO) {
86+
val compat = RunAnywhere.loraAdaptersForModel(modelId)
87+
Triple(compat, RunAnywhere.getLoadedLoraAdapters(), scanDownloadedAdapters(compat))
8488
}
8589
_uiState.update {
8690
it.copy(
8791
compatibleAdapters = compatible,
8892
loadedAdapters = loaded,
93+
downloadedAdapterPaths = it.downloadedAdapterPaths + downloaded,
8994
error = null,
9095
)
9196
}
@@ -151,15 +156,14 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
151156
}
152157
}
153158

154-
/** Get the local file path for a catalog entry, or null if not downloaded. */
159+
/** Get the local file path for a catalog entry, or null if not downloaded (reads from cached state). */
155160
fun localPath(entry: LoraAdapterCatalogEntry): String? {
156-
val file = File(loraDir, entry.filename)
157-
return if (file.exists()) file.absolutePath else null
161+
return _uiState.value.downloadedAdapterPaths[entry.id]
158162
}
159163

160-
/** Check if a catalog entry is already downloaded. */
164+
/** Check if a catalog entry is already downloaded (reads from cached state). */
161165
fun isDownloaded(entry: LoraAdapterCatalogEntry): Boolean {
162-
return File(loraDir, entry.filename).exists()
166+
return entry.id in _uiState.value.downloadedAdapterPaths
163167
}
164168

165169
/** Check if a specific adapter is currently loaded. */
@@ -168,6 +172,14 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
168172
return _uiState.value.loadedAdapters.any { it.path == path }
169173
}
170174

175+
/** Scan disk for downloaded adapter files and return id->path map. Must be called on IO dispatcher. */
176+
private fun scanDownloadedAdapters(adapters: List<LoraAdapterCatalogEntry>): Map<String, String> {
177+
return adapters.mapNotNull { entry ->
178+
val file = File(loraDir, entry.filename)
179+
if (file.exists()) entry.id to file.absolutePath else null
180+
}.toMap()
181+
}
182+
171183
/** Download a LoRA adapter GGUF file. */
172184
fun downloadAdapter(entry: LoraAdapterCatalogEntry) {
173185
if (_uiState.value.downloadingAdapterId != null) return
@@ -221,6 +233,7 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
221233
it.copy(
222234
downloadingAdapterId = null,
223235
downloadProgress = 0f,
236+
downloadedAdapterPaths = it.downloadedAdapterPaths + (entry.id to destFile.absolutePath),
224237
)
225238
}
226239
} catch (e: Exception) {
@@ -269,7 +282,12 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
269282
}
270283
}
271284
val loaded = withContext(Dispatchers.IO) { RunAnywhere.getLoadedLoraAdapters() }
272-
_uiState.update { it.copy(loadedAdapters = loaded) }
285+
_uiState.update {
286+
it.copy(
287+
loadedAdapters = loaded,
288+
downloadedAdapterPaths = it.downloadedAdapterPaths - entry.id,
289+
)
290+
}
273291
} catch (e: Exception) {
274292
Timber.e(e, "Failed to delete adapter: ${entry.filename}")
275293
_uiState.update { it.copy(error = "Delete failed: ${e.message}") }

sdk/runanywhere-commons/include/rac/core/rac_core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "rac/core/rac_error.h"
1313
#include "rac/core/rac_types.h"
1414
#include "rac/infrastructure/model_management/rac_model_types.h"
15+
#include "rac/infrastructure/model_management/rac_lora_registry.h"
1516
#include "rac/infrastructure/network/rac_environment.h"
1617

1718
#ifdef __cplusplus

sdk/runanywhere-commons/src/infrastructure/model_management/lora_registry.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,23 @@ static rac_lora_entry_t* deep_copy_lora_entry(const rac_lora_entry_t* src) {
3030
copy->description = rac_strdup(src->description);
3131
copy->download_url = rac_strdup(src->download_url);
3232
copy->filename = rac_strdup(src->filename);
33+
34+
// id is required — if it failed to copy, the entry is unusable
35+
if (src->id && !copy->id) {
36+
free_lora_entry(copy);
37+
return nullptr;
38+
}
39+
3340
if (src->compatible_model_ids && src->compatible_model_count > 0) {
3441
copy->compatible_model_ids = static_cast<char**>(malloc(sizeof(char*) * src->compatible_model_count));
35-
if (copy->compatible_model_ids) {
36-
for (size_t i = 0; i < src->compatible_model_count; ++i) {
37-
copy->compatible_model_ids[i] = rac_strdup(src->compatible_model_ids[i]);
38-
}
39-
copy->compatible_model_count = src->compatible_model_count;
42+
if (!copy->compatible_model_ids) {
43+
free_lora_entry(copy);
44+
return nullptr;
45+
}
46+
for (size_t i = 0; i < src->compatible_model_count; ++i) {
47+
copy->compatible_model_ids[i] = rac_strdup(src->compatible_model_ids[i]);
4048
}
49+
copy->compatible_model_count = src->compatible_model_count;
4150
}
4251
copy->file_size = src->file_size;
4352
copy->default_scale = src->default_scale;

0 commit comments

Comments
 (0)