Skip to content

Commit 8a65ccc

Browse files
minor update
1 parent 9d9d730 commit 8a65ccc

2 files changed

Lines changed: 42 additions & 48 deletions

File tree

sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeModelRegistry.kt

Lines changed: 15 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package com.runanywhere.sdk.foundation.bridge.extensions
1515

1616
import com.runanywhere.sdk.native.bridge.RunAnywhereBridge
17+
import java.io.File
1718

1819
/**
1920
* Model registry bridge that provides direct access to the C++ model registry.
@@ -215,7 +216,7 @@ object CppBridgeModelRegistry {
215216
}
216217

217218
/**
218-
* Update download status in C++ registry.
219+
* Update download status in C++ registry (in-memory only).
219220
*
220221
* @param modelId The model ID
221222
* @param localPath The local path (or null to clear download)
@@ -259,71 +260,43 @@ object CppBridgeModelRegistry {
259260
}
260261

261262
/**
262-
* Scan filesystem and restore downloaded models.
263+
* Scan filesystem and restore downloaded models whose filename matches their model ID.
264+
* This handles single-file models (GGUF, ONNX) and archive models that extracted into
265+
* a named directory matching the model ID.
263266
*
264-
* This is called during SDK initialization to detect previously
265-
* downloaded models and update their status in the C++ registry.
267+
* For archive models with flat extraction (e.g. Genie), see
268+
* [RunAnywhere.restorePersistedDownloadPaths] in RunAnywhere+ModelManagement.jvmAndroid.kt.
266269
*/
267270
fun scanAndRestoreDownloadedModels() {
268-
log(LogLevel.DEBUG, "Scanning for previously downloaded models...")
269-
270271
val baseDir = CppBridgeModelPaths.getBaseDirectory()
271-
val modelsDir = java.io.File(baseDir, "models")
272+
val modelsDir = File(baseDir, "models")
272273

273274
if (!modelsDir.exists()) {
274275
log(LogLevel.DEBUG, "Models directory does not exist: ${modelsDir.absolutePath}")
275276
return
276277
}
277278

278-
val typeDirectories =
279-
mapOf(
280-
"llm" to ModelCategory.LANGUAGE,
281-
"stt" to ModelCategory.SPEECH_RECOGNITION,
282-
"tts" to ModelCategory.SPEECH_SYNTHESIS,
283-
"vad" to ModelCategory.AUDIO,
284-
285-
// RAG
286-
"embedding" to ModelType.EMBEDDING,
287-
288-
// Vision / VLM
289-
"vision" to ModelCategory.VISION,
290-
"multimodal" to ModelCategory.MULTIMODAL,
291-
292-
// Backward compatibility
293-
"other" to -1,
294-
)
295-
279+
log(LogLevel.DEBUG, "Scanning for previously downloaded models...")
296280
var restoredCount = 0
297281

298-
for ((dirName, _) in typeDirectories) {
299-
val typeDir = java.io.File(modelsDir, dirName)
282+
val typeDirectories = listOf("llm", "stt", "tts", "vad", "embedding", "vision", "multimodal", "other")
283+
for (dirName in typeDirectories) {
284+
val typeDir = File(modelsDir, dirName)
300285
if (!typeDir.exists() || !typeDir.isDirectory) continue
301286

302-
log(LogLevel.DEBUG, "Scanning type directory: ${typeDir.absolutePath}")
303-
304-
// Scan each model file or folder in this type directory
305287
typeDir.listFiles()?.forEach { modelPath ->
306-
// Model can be stored as:
307-
// 1. A directory containing the model (e.g., models/llm/model-name/)
308-
// 2. A file directly (e.g., models/llm/model-name)
309288
val modelId = modelPath.name
310-
log(LogLevel.DEBUG, "Found: $modelId (isDir=${modelPath.isDirectory}, isFile=${modelPath.isFile})")
311-
312-
// Check if this model exists in registry
313289
val existingModel = get(modelId)
314-
if (existingModel != null) {
315-
// Update with local path
290+
if (existingModel != null && existingModel.localPath == null) {
316291
if (updateDownloadStatus(modelId, modelPath.absolutePath)) {
317292
restoredCount++
318-
log(LogLevel.DEBUG, "Restored downloaded model: $modelId at ${modelPath.absolutePath}")
293+
log(LogLevel.DEBUG, "Restored $modelId at ${modelPath.absolutePath}")
319294
}
320-
} else {
321-
log(LogLevel.DEBUG, "Model $modelId not found in registry, skipping")
322295
}
323296
}
324297
}
325298

326-
log(LogLevel.INFO, "Scan complete: Restored $restoredCount previously downloaded models")
299+
log(LogLevel.INFO, "Filesystem scan complete: restored $restoredCount models")
327300
}
328301

329302
// ========================================================================

sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+ModelManagement.jvmAndroid.kt

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import com.runanywhere.sdk.foundation.bridge.extensions.CppBridgeLLM
1515
import com.runanywhere.sdk.foundation.bridge.extensions.CppBridgeModelPaths
1616
import com.runanywhere.sdk.foundation.bridge.extensions.CppBridgeModelRegistry
1717
import com.runanywhere.sdk.foundation.bridge.extensions.CppBridgeSTT
18+
import com.runanywhere.sdk.foundation.bridge.extensions.CppBridgeStorage
1819
import com.runanywhere.sdk.foundation.errors.SDKError
1920
import com.runanywhere.sdk.public.RunAnywhere
2021
import com.runanywhere.sdk.public.extensions.Models.DownloadProgress
@@ -508,6 +509,7 @@ actual fun RunAnywhere.downloadModel(modelId: String): Flow<DownloadProgress> {
508509
val updatedModelInfo = modelInfo.copy(localPath = finalPath)
509510
addToModelCache(updatedModelInfo)
510511
CppBridgeModelRegistry.updateDownloadStatus(modelId, finalPath)
512+
CppBridgeStorage.storeString(CppBridgeStorage.StorageNamespace.DOWNLOADS, modelId, finalPath)
511513

512514
downloadLogger.info("Multi-file model ready at: $finalPath")
513515

@@ -724,6 +726,7 @@ actual fun RunAnywhere.downloadModel(modelId: String): Flow<DownloadProgress> {
724726
val updatedModelInfo = modelInfo.copy(localPath = finalModelPath)
725727
addToModelCache(updatedModelInfo)
726728
CppBridgeModelRegistry.updateDownloadStatus(modelId, finalModelPath)
729+
CppBridgeStorage.storeString(CppBridgeStorage.StorageNamespace.DOWNLOADS, modelId, finalModelPath)
727730

728731
downloadLogger.info("Model ready at: $finalModelPath")
729732

@@ -813,6 +816,9 @@ private suspend fun extractArchive(
813816
// Use the URL to determine archive type (file may be saved without extension)
814817
val lowercaseUrl = originalUrl.lowercase()
815818

819+
// Snapshot existing items BEFORE extraction to detect newly extracted flat files
820+
val itemsBeforeExtraction = parentDir.listFiles()?.map { it.name }?.toSet() ?: emptySet()
821+
816822
// IMPORTANT: The archive file name might conflict with the folder inside the archive
817823
// (e.g., file "sherpa-onnx-whisper-tiny.en" and archive contains folder "sherpa-onnx-whisper-tiny.en/")
818824
// We need to rename/move the archive before extracting to avoid ENOTDIR errors
@@ -867,14 +873,27 @@ private suspend fun extractArchive(
867873
val expectedModelDir = File(parentDir, modelId)
868874
val finalPath =
869875
if (expectedModelDir.exists() && expectedModelDir.isDirectory) {
876+
// Standard case: archive had a named root folder (ONNX, LlamaCpp archives)
870877
expectedModelDir.absolutePath
871878
} else {
872-
// Fallback: look for any new directory created
873-
parentDir
874-
.listFiles()
875-
?.firstOrNull {
876-
it.isDirectory && it.name.contains(modelId.substringBefore("-"))
877-
}?.absolutePath ?: parentDir.absolutePath
879+
// Fallback: look for any new named directory matching the model ID prefix
880+
val matchingDir = parentDir.listFiles()
881+
?.firstOrNull { it.isDirectory && it.name.contains(modelId.substringBefore("-")) }
882+
883+
if (matchingDir != null) {
884+
matchingDir.absolutePath
885+
} else {
886+
// Flat archive (e.g. Genie): files extracted directly into parentDir.
887+
// Move them into a per-model subdirectory so the standard filesystem
888+
// scan can find and restore this model by its ID across app restarts.
889+
expectedModelDir.mkdirs()
890+
val newItems = parentDir.listFiles()
891+
?.filter { it.name !in itemsBeforeExtraction && it != expectedModelDir }
892+
?: emptyList()
893+
newItems.forEach { file -> file.renameTo(File(expectedModelDir, file.name)) }
894+
logger.info("Moved ${newItems.size} flat-extracted files into: ${expectedModelDir.absolutePath}")
895+
expectedModelDir.absolutePath
896+
}
878897
}
879898

880899
logger.info("Model extracted to: $finalPath")
@@ -1065,6 +1084,7 @@ private suspend fun downloadEmbeddingModelFiles(
10651084
}
10661085
}
10671086
CppBridgeModelRegistry.updateDownloadStatus(modelId, dirPath)
1087+
CppBridgeStorage.storeString(CppBridgeStorage.StorageNamespace.DOWNLOADS, modelId, dirPath)
10681088
CppBridgeEvents.emitDownloadCompleted(modelId, 0.0, 0)
10691089

10701090
logger.info("Embedding model ready at: $dirPath")
@@ -1143,6 +1163,7 @@ actual suspend fun RunAnywhere.deleteModel(modelId: String) {
11431163
if (!isInitialized) {
11441164
throw SDKError.notInitialized("SDK not initialized")
11451165
}
1166+
CppBridgeStorage.delete(CppBridgeStorage.StorageNamespace.DOWNLOADS, modelId)
11461167
CppBridgeModelRegistry.remove(modelId)
11471168
}
11481169

0 commit comments

Comments
 (0)