Skip to content

Commit 3e75c4b

Browse files
authored
feat(model-management): Add device compatibility check for models using RACommons (#357)
1 parent eb0c5fb commit 3e75c4b

File tree

18 files changed

+477
-7
lines changed

18 files changed

+477
-7
lines changed

sdk/runanywhere-commons/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ set(RAC_INFRASTRUCTURE_SOURCES
205205
src/infrastructure/model_management/model_paths.cpp
206206
src/infrastructure/model_management/model_strategy.cpp
207207
src/infrastructure/model_management/model_assignment.cpp
208+
src/infrastructure/model_management/model_compatibility.cpp
208209
src/infrastructure/storage/storage_analyzer.cpp
209210
src/infrastructure/network/environment.cpp
210211
src/infrastructure/network/endpoints.cpp

sdk/runanywhere-commons/exports/RACommons.exports

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ _rac_model_info_copy
118118
_rac_model_info_free
119119
_rac_model_info_is_downloaded
120120
_rac_model_matches_filter
121+
_rac_model_check_compatibility
121122

122123
# Model Paths
123124
_rac_model_paths_extract_framework
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* @file rac_model_compatibility.h
3+
* @brief Model Compatibility Check - Checks device RAM/storage against model requirements
4+
*
5+
* Minimalist check: compares the model's memory_required and download_size
6+
* against the device's available RAM and free storage.
7+
*/
8+
9+
#ifndef RAC_MODEL_COMPATIBILITY_H
10+
#define RAC_MODEL_COMPATIBILITY_H
11+
12+
#include "rac/core/rac_types.h"
13+
#include "rac/infrastructure/model_management/rac_model_registry.h"
14+
15+
#ifdef __cplusplus
16+
extern "C" {
17+
#endif
18+
19+
/**
20+
* @brief Result of a model compatibility check
21+
*/
22+
typedef struct rac_model_compatibility_result {
23+
/** Overall compatibility (canRun AND canFit) */
24+
rac_bool_t is_compatible;
25+
26+
/** Whether the device has enough RAM to run the model */
27+
rac_bool_t can_run;
28+
29+
/** Whether the device has enough free storage to download/store the model */
30+
rac_bool_t can_fit;
31+
32+
/** Model's required RAM in bytes (from model registry) */
33+
int64_t required_memory;
34+
35+
/** Device's available RAM in bytes */
36+
int64_t available_memory;
37+
38+
/** Model's download/storage size in bytes (from model registry) */
39+
int64_t required_storage;
40+
41+
/** Device's available storage in bytes */
42+
int64_t available_storage;
43+
} rac_model_compatibility_result_t;
44+
45+
/**
46+
* @brief Check if a model is compatible with the current device
47+
*
48+
* Looks up the model in the registry, reads its memory_required and download_size,
49+
* then compares against the provided available RAM and storage values.
50+
*
51+
* @param registry_handle Model registry handle (to look up model metadata)
52+
* @param model_id Model identifier string
53+
* @param available_ram Available RAM in bytes (provided by caller)
54+
* @param available_storage Available storage in bytes (provided by caller)
55+
* @param out_result Output: compatibility result
56+
* @return RAC_SUCCESS, RAC_ERROR_NOT_FOUND if model not in registry, or other error
57+
*/
58+
RAC_API rac_result_t rac_model_check_compatibility(
59+
rac_model_registry_handle_t registry_handle,
60+
const char* model_id,
61+
int64_t available_ram,
62+
int64_t available_storage,
63+
rac_model_compatibility_result_t* out_result);
64+
65+
#ifdef __cplusplus
66+
}
67+
#endif
68+
69+
#endif /* RAC_MODEL_COMPATIBILITY_H */
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* @file model_compatibility.cpp
3+
* @brief Implementation of model compatibility checks
4+
*
5+
* C++ implementation. The C API is declared in the header with extern "C".
6+
* Follows the same pattern as model_paths.cpp, model_registry.cpp, etc.
7+
*/
8+
9+
#include "rac/infrastructure/model_management/rac_model_compatibility.h"
10+
#include "rac/core/rac_logger.h"
11+
#include "rac/infrastructure/model_management/rac_model_registry.h"
12+
13+
#include <cstring>
14+
15+
// =============================================================================
16+
// COMPATIBILITY CHECK IMPLEMENTATION
17+
// =============================================================================
18+
19+
rac_result_t rac_model_check_compatibility(
20+
rac_model_registry_handle_t registry_handle,
21+
const char* model_id,
22+
int64_t available_ram,
23+
int64_t available_storage,
24+
rac_model_compatibility_result_t* out_result) {
25+
26+
if (!registry_handle || !model_id || !out_result) {
27+
RAC_LOG_ERROR("ModelCompatibility", "Invalid arguments");
28+
return RAC_ERROR_INVALID_ARGUMENT;
29+
}
30+
31+
// Zero-initialize the result
32+
std::memset(out_result, 0, sizeof(rac_model_compatibility_result_t));
33+
34+
// Look up the model in the registry
35+
rac_model_info_t* model = nullptr;
36+
rac_result_t result = rac_model_registry_get(registry_handle, model_id, &model);
37+
38+
if (result != RAC_SUCCESS) {
39+
RAC_LOG_WARNING("ModelCompatibility",
40+
"Failed to get model from registry: %s (error: %d)",
41+
model_id, result);
42+
return result;
43+
}
44+
45+
if (!model) {
46+
RAC_LOG_WARNING("ModelCompatibility", "Model not found: %s", model_id);
47+
return RAC_ERROR_NOT_FOUND;
48+
}
49+
50+
// Extract model requirements
51+
int64_t required_memory = model->memory_required; // bytes
52+
int64_t required_storage = model->download_size; // bytes
53+
54+
RAC_LOG_DEBUG("ModelCompatibility",
55+
"Model %s requirements: memory=%lld bytes, storage=%lld bytes",
56+
model_id,
57+
static_cast<long long>(required_memory),
58+
static_cast<long long>(required_storage));
59+
60+
// Determine compatibility
61+
// can_run: available RAM >= required memory (or requirement is 0/unknown)
62+
// can_fit: available storage >= required storage (or requirement is 0/unknown)
63+
rac_bool_t can_run = (required_memory <= 0 || available_ram >= required_memory)
64+
? RAC_TRUE : RAC_FALSE;
65+
rac_bool_t can_fit = (required_storage <= 0 || available_storage >= required_storage)
66+
? RAC_TRUE : RAC_FALSE;
67+
68+
// Populate result
69+
out_result->can_run = can_run;
70+
out_result->can_fit = can_fit;
71+
out_result->is_compatible = (can_run == RAC_TRUE && can_fit == RAC_TRUE)
72+
? RAC_TRUE : RAC_FALSE;
73+
out_result->required_memory = required_memory;
74+
out_result->available_memory = available_ram;
75+
out_result->required_storage = required_storage;
76+
out_result->available_storage = available_storage;
77+
78+
RAC_LOG_INFO("ModelCompatibility",
79+
"Model %s: canRun=%d canFit=%d isCompatible=%d "
80+
"(RAM: %lld/%lld, Storage: %lld/%lld)",
81+
model_id,
82+
can_run, can_fit, out_result->is_compatible,
83+
static_cast<long long>(available_ram),
84+
static_cast<long long>(required_memory),
85+
static_cast<long long>(available_storage),
86+
static_cast<long long>(required_storage));
87+
88+
// Free the model info (allocated by rac_model_registry_get)
89+
rac_model_info_free(model);
90+
91+
return RAC_SUCCESS;
92+
}

sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "bridges/AuthBridge.hpp"
3232
#include "bridges/StorageBridge.hpp"
3333
#include "bridges/ModelRegistryBridge.hpp"
34+
#include "bridges/CompatibilityBridge.hpp"
3435
#include "bridges/EventBridge.hpp"
3536
#include "bridges/HTTPBridge.hpp"
3637
#include "bridges/DownloadBridge.hpp"
@@ -1007,10 +1008,38 @@ std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::registerModel(
10071008
});
10081009
}
10091010

1011+
// ============================================================================
1012+
// Compatibility Service
1013+
// ============================================================================
1014+
1015+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::checkCompatibility(
1016+
const std::string& modelId) {
1017+
return Promise<std::string>::async([modelId]() -> std::string {
1018+
auto registryHandle = ModelRegistryBridge::shared().getHandle();
1019+
if (!registryHandle) {
1020+
LOGE("Model registry not initialized");
1021+
return "{}";
1022+
}
1023+
1024+
// Delegate to CompatibilityBridge - it handles querying device capabilities
1025+
auto result = CompatibilityBridge::checkCompatibility(modelId, registryHandle);
1026+
1027+
return buildJsonObject({
1028+
{"isCompatible", result.isCompatible ? "true" : "false"},
1029+
{"canRun", result.canRun ? "true" : "false"},
1030+
{"canFit", result.canFit ? "true" : "false"},
1031+
{"requiredMemory", std::to_string(result.requiredMemory)},
1032+
{"availableMemory", std::to_string(result.availableMemory)},
1033+
{"requiredStorage", std::to_string(result.requiredStorage)},
1034+
{"availableStorage", std::to_string(result.availableStorage)}
1035+
});
1036+
});
1037+
}
10101038
// ============================================================================
10111039
// Download Service
10121040
// ============================================================================
10131041

1042+
10141043
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::downloadModel(
10151044
const std::string& modelId,
10161045
const std::string& url,

sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class HybridRunAnywhereCore : public HybridRunAnywhereCoreSpec {
9292
std::shared_ptr<Promise<bool>> isModelDownloaded(const std::string& modelId) override;
9393
std::shared_ptr<Promise<std::string>> getModelPath(const std::string& modelId) override;
9494
std::shared_ptr<Promise<bool>> registerModel(const std::string& modelJson) override;
95+
std::shared_ptr<Promise<std::string>> checkCompatibility(const std::string& modelId) override;
9596

9697
// ============================================================================
9798
// Download Service - Delegates to DownloadBridge
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* @file CompatibilityBridge.cpp
3+
* @brief C++ bridge for model compatibility checks.
4+
*
5+
* Uses DeviceBridge for RAM and POSIX statvfs for disk space,
6+
* then calls rac_model_check_compatibility() from runanywhere-commons.
7+
*/
8+
9+
#include "CompatibilityBridge.hpp"
10+
#include "DeviceBridge.hpp"
11+
12+
#include <sys/statvfs.h> // POSIX filesystem statistics
13+
14+
// Platform-specific logging
15+
#if defined(ANDROID) || defined(__ANDROID__)
16+
#include <android/log.h>
17+
#define LOG_TAG "CompatibilityBridge"
18+
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
19+
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
20+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
21+
#else
22+
#include <cstdio>
23+
#define LOGI(...) printf("[CompatibilityBridge] "); printf(__VA_ARGS__); printf("\n")
24+
#define LOGD(...) printf("[CompatibilityBridge DEBUG] "); printf(__VA_ARGS__); printf("\n")
25+
#define LOGE(...) printf("[CompatibilityBridge ERROR] "); printf(__VA_ARGS__); printf("\n")
26+
#endif
27+
28+
namespace runanywhere {
29+
namespace bridges {
30+
31+
CompatibilityResult CompatibilityBridge::checkCompatibility(
32+
const std::string& modelId,
33+
rac_model_registry_handle_t registryHandle) {
34+
35+
CompatibilityResult result;
36+
37+
if (!registryHandle) {
38+
LOGE("Model registry handle is null");
39+
return result;
40+
}
41+
42+
// Get available RAM from DeviceBridge
43+
int64_t availableRAM = 0;
44+
if (DeviceBridge::shared().isCallbacksRegistered()) {
45+
auto deviceInfo = DeviceBridge::shared().getDeviceInfo();
46+
availableRAM = deviceInfo.availableMemory;
47+
LOGD("Available RAM from DeviceBridge: %lld bytes",
48+
static_cast<long long>(availableRAM));
49+
} else {
50+
LOGD("DeviceBridge not initialized, RAM check will be skipped");
51+
}
52+
53+
// Get available storage using POSIX statvfs
54+
// This queries the filesystem directly - same as what FileManager/StatFs use underneath
55+
int64_t availableStorage = 0;
56+
{
57+
struct statvfs stat;
58+
// Query root filesystem - works on both iOS and Android
59+
if (statvfs("/", &stat) == 0) {
60+
// f_bavail = available blocks for unprivileged users
61+
// f_frsize = fragment size (fundamental block size)
62+
availableStorage = static_cast<int64_t>(stat.f_bavail) * static_cast<int64_t>(stat.f_frsize);
63+
64+
LOGD("Available storage from statvfs: %lld bytes (%.2f GB)",
65+
static_cast<long long>(availableStorage),
66+
static_cast<double>(availableStorage) / (1024.0 * 1024.0 * 1024.0));
67+
} else {
68+
LOGE("statvfs failed (errno=%d), storage check will be skipped", errno);
69+
}
70+
}
71+
72+
// Call the RACommons C API
73+
rac_model_compatibility_result_t cResult;
74+
rac_result_t rc = rac_model_check_compatibility(
75+
registryHandle,
76+
modelId.c_str(),
77+
availableRAM,
78+
availableStorage,
79+
&cResult);
80+
81+
if (rc == RAC_SUCCESS) {
82+
result.isCompatible = cResult.is_compatible == RAC_TRUE;
83+
result.canRun = cResult.can_run == RAC_TRUE;
84+
result.canFit = cResult.can_fit == RAC_TRUE;
85+
result.requiredMemory = cResult.required_memory;
86+
result.availableMemory = cResult.available_memory;
87+
result.requiredStorage = cResult.required_storage;
88+
result.availableStorage = cResult.available_storage;
89+
90+
LOGI("Compatibility check for %s: compatible=%d, canRun=%d, canFit=%d, RAM=%lld/%lld, Storage=%lld/%lld",
91+
modelId.c_str(),
92+
result.isCompatible,
93+
result.canRun,
94+
result.canFit,
95+
static_cast<long long>(result.availableMemory),
96+
static_cast<long long>(result.requiredMemory),
97+
static_cast<long long>(result.availableStorage),
98+
static_cast<long long>(result.requiredStorage));
99+
} else {
100+
LOGE("Compatibility check failed for %s: error %d", modelId.c_str(), rc);
101+
}
102+
103+
return result;
104+
}
105+
106+
} // namespace bridges
107+
} // namespace runanywhere

0 commit comments

Comments
 (0)