Skip to content

Commit 5f9adca

Browse files
kotlin fix
1 parent 3bec401 commit 5f9adca

File tree

3 files changed

+141
-22
lines changed

3 files changed

+141
-22
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ navigationCompose = "2.8.5"
6868
# ============================================================================
6969
# Code Quality
7070
# ============================================================================
71-
detekt = "1.23.7"
71+
detekt = "1.23.8"
7272
ktlint = "12.1.2"
7373

7474
# ============================================================================

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

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ import com.runanywhere.sdk.foundation.bridge.extensions.CppBridgeTelemetry
2121
import com.runanywhere.sdk.foundation.logging.SentryDestination
2222
import com.runanywhere.sdk.foundation.logging.SentryManager
2323
import com.runanywhere.sdk.native.bridge.RunAnywhereBridge
24+
import kotlinx.coroutines.CoroutineScope
25+
import kotlinx.coroutines.Dispatchers
26+
import kotlinx.coroutines.launch
2427

2528
/**
2629
* CppBridge is the central coordinator for all C++ interop via JNI.
@@ -396,9 +399,43 @@ object CppBridge {
396399
}
397400

398401
// Step 2: Register model assignment callbacks
399-
// Only auto-fetch in staging/production, not development
400-
val shouldAutoFetch = _environment != Environment.DEVELOPMENT
401-
CppBridgeModelAssignment.register(autoFetch = shouldAutoFetch)
402+
// IMPORTANT: Register WITHOUT auto-fetch first to avoid threading issues
403+
// The C++ auto-fetch mechanism can cause state issues when called during JNI registration
404+
// This mirrors Swift SDK which always registers with autoFetch: false
405+
logger.info("========== STEP 2: MODEL ASSIGNMENT REGISTRATION ==========")
406+
val shouldFetchModels = _environment != Environment.DEVELOPMENT
407+
logger.info("📦 Environment: ${_environment.name}, shouldFetchModels: $shouldFetchModels")
408+
logger.info("📦 Registering model assignment callbacks (autoFetch: false)")
409+
val registrationSucceeded = CppBridgeModelAssignment.register(autoFetch = false) // Always false!
410+
logger.info("📦 Registration result: $registrationSucceeded")
411+
412+
// If auto-fetch is needed, trigger it asynchronously off the synchronized block
413+
// This mirrors Swift SDK's Task.detached pattern:
414+
// Task.detached {
415+
// _ = try await ModelAssignment.fetch(forceRefresh: true)
416+
// }
417+
// By fetching asynchronously, we:
418+
// 1. Avoid blocking the initialization thread
419+
// 2. Ensure callbacks are fully registered before HTTP fetch begins
420+
if (shouldFetchModels && registrationSucceeded) {
421+
logger.info("📦 Will fetch model assignments asynchronously...")
422+
CoroutineScope(Dispatchers.IO).launch {
423+
try {
424+
logger.info("📦 [Async] Fetching model assignments from backend (forceRefresh=true)...")
425+
val assignmentsJson = CppBridgeModelAssignment.fetchModelAssignments(forceRefresh = true)
426+
logger.info("📦 [Async] Model assignments fetched successfully (${assignmentsJson.length} chars)")
427+
logger.info("📦 [Async] Response: ${assignmentsJson.take(500)}...")
428+
} catch (e: Exception) {
429+
logger.warn("📦 [Async] Model assignment fetch failed (non-critical): ${e.message}")
430+
}
431+
}
432+
} else if (!registrationSucceeded) {
433+
logger.error("📦 Skipping model assignment fetch - callback registration failed")
434+
logger.error("📦 This may indicate a JNI method signature mismatch or native library issue")
435+
} else {
436+
logger.info("📦 Skipping model fetch (development mode)")
437+
}
438+
logger.info("========== STEP 2 COMPLETE ==========")
402439

403440
// Register platform services callbacks
404441
CppBridgePlatform.register()

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

Lines changed: 100 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -300,27 +300,82 @@ object CppBridgeModelAssignment {
300300
?: "https://api.runanywhere.ai"
301301
val fullUrl = "$baseUrl$endpoint"
302302

303-
// Build headers
303+
CppBridgePlatformAdapter.logCallback(
304+
CppBridgePlatformAdapter.LogLevel.INFO,
305+
TAG,
306+
">>> Model assignment HTTP GET to: $fullUrl (requiresAuth: $requiresAuth)",
307+
)
308+
CppBridgePlatformAdapter.logCallback(
309+
CppBridgePlatformAdapter.LogLevel.INFO,
310+
TAG,
311+
">>> Base URL: $baseUrl, Endpoint: $endpoint",
312+
)
313+
314+
// Build headers - matching Swift SDK's HTTPService.defaultHeaders
304315
val headers = mutableMapOf<String, String>()
305316
headers["Accept"] = "application/json"
306317
headers["Content-Type"] = "application/json"
318+
headers["X-SDK-Client"] = "RunAnywhereSDK"
319+
headers["X-SDK-Version"] = com.runanywhere.sdk.utils.SDKConstants.SDK_VERSION
320+
headers["X-Platform"] = "android"
307321

308322
if (requiresAuth) {
309323
// Get access token from auth manager
324+
CppBridgePlatformAdapter.logCallback(
325+
CppBridgePlatformAdapter.LogLevel.INFO,
326+
TAG,
327+
"Auth state - isAuthenticated: ${CppBridgeAuth.isAuthenticated}, tokenNeedsRefresh: ${CppBridgeAuth.tokenNeedsRefresh}",
328+
)
310329
val accessToken = CppBridgeAuth.getValidToken()
311330
if (!accessToken.isNullOrEmpty()) {
312331
headers["Authorization"] = "Bearer $accessToken"
332+
CppBridgePlatformAdapter.logCallback(
333+
CppBridgePlatformAdapter.LogLevel.INFO,
334+
TAG,
335+
"Added Authorization header (token length: ${accessToken.length}, starts: ${accessToken.take(30)}...)",
336+
)
337+
} else {
338+
// Fallback to API key if no OAuth token available
339+
// This mirrors Swift SDK's HTTPService.resolveToken() behavior
340+
val apiKey = CppBridgeTelemetry.getApiKey()
341+
if (!apiKey.isNullOrEmpty()) {
342+
headers["Authorization"] = "Bearer $apiKey"
343+
CppBridgePlatformAdapter.logCallback(
344+
CppBridgePlatformAdapter.LogLevel.INFO,
345+
TAG,
346+
"No OAuth token available, falling back to API key authentication (key length: ${apiKey.length})",
347+
)
348+
} else {
349+
CppBridgePlatformAdapter.logCallback(
350+
CppBridgePlatformAdapter.LogLevel.ERROR,
351+
TAG,
352+
"⚠️ No access token or API key available for authenticated request! Model assignments will likely fail.",
353+
)
354+
}
313355
}
314356
}
315357

316358
// Make HTTP request
317359
val response = CppBridgeHTTP.get(fullUrl, headers)
318360

361+
CppBridgePlatformAdapter.logCallback(
362+
CppBridgePlatformAdapter.LogLevel.INFO,
363+
TAG,
364+
"<<< Model assignment response: status=${response.statusCode}, success=${response.success}, bodyLen=${response.body?.length ?: 0}",
365+
)
366+
367+
// Log full response body for debugging
368+
CppBridgePlatformAdapter.logCallback(
369+
CppBridgePlatformAdapter.LogLevel.INFO,
370+
TAG,
371+
"<<< Full response body: ${response.body ?: "null"}",
372+
)
373+
319374
if (response.success && response.body != null) {
320375
CppBridgePlatformAdapter.logCallback(
321-
CppBridgePlatformAdapter.LogLevel.DEBUG,
376+
CppBridgePlatformAdapter.LogLevel.INFO,
322377
TAG,
323-
"Model assignments fetched successfully",
378+
"Model assignments fetched successfully: ${response.body.take(500)}",
324379
)
325380
response.body
326381
} else {
@@ -351,47 +406,56 @@ object CppBridgeModelAssignment {
351406
*
352407
* @param autoFetch Whether to auto-fetch models after registration.
353408
* Should be false for development mode, true for staging/production.
409+
* @return true if registration succeeded, false otherwise
354410
*/
355-
fun register(autoFetch: Boolean = false) {
411+
fun register(autoFetch: Boolean = false): Boolean {
356412
synchronized(lock) {
357413
if (isRegistered) {
358-
return
414+
CppBridgePlatformAdapter.logCallback(
415+
CppBridgePlatformAdapter.LogLevel.DEBUG,
416+
TAG,
417+
"Model assignment callbacks already registered, skipping",
418+
)
419+
return true
359420
}
360421

361422
// Register the model assignment callbacks with C++ via JNI
362423
// auto_fetch controls whether models are fetched immediately after registration
363424
try {
425+
CppBridgePlatformAdapter.logCallback(
426+
CppBridgePlatformAdapter.LogLevel.INFO,
427+
TAG,
428+
"Registering model assignment callbacks with C++ (autoFetch: $autoFetch)...",
429+
)
430+
364431
val result = com.runanywhere.sdk.native.bridge.RunAnywhereBridge
365432
.racModelAssignmentSetCallbacks(nativeCallbackHandler, autoFetch)
366433

367434
if (result == 0) { // RAC_SUCCESS
435+
isRegistered = true
368436
CppBridgePlatformAdapter.logCallback(
369437
CppBridgePlatformAdapter.LogLevel.INFO,
370438
TAG,
371-
"Model assignment callbacks registered (autoFetch: $autoFetch)",
439+
"Model assignment callbacks registered successfully (autoFetch: $autoFetch)",
372440
)
441+
return true
373442
} else {
374443
CppBridgePlatformAdapter.logCallback(
375444
CppBridgePlatformAdapter.LogLevel.ERROR,
376445
TAG,
377-
"Failed to register model assignment callbacks: $result",
446+
"❌ Failed to register model assignment callbacks: error code $result " +
447+
"(RAC_ERROR_INVALID_ARGUMENT=-201, RAC_ERROR_INVALID_STATE=-231)",
378448
)
449+
return false
379450
}
380451
} catch (e: Exception) {
381452
CppBridgePlatformAdapter.logCallback(
382453
CppBridgePlatformAdapter.LogLevel.ERROR,
383454
TAG,
384-
"Exception registering model assignment callbacks: ${e.message}",
455+
"Exception registering model assignment callbacks: ${e.message}",
385456
)
457+
return false
386458
}
387-
388-
isRegistered = true
389-
390-
CppBridgePlatformAdapter.logCallback(
391-
CppBridgePlatformAdapter.LogLevel.DEBUG,
392-
TAG,
393-
"Model assignment callbacks registered",
394-
)
395459
}
396460
}
397461

@@ -919,20 +983,38 @@ object CppBridgeModelAssignment {
919983
* @return JSON string of model assignments, or empty array on error
920984
*/
921985
fun fetchModelAssignments(forceRefresh: Boolean = false): String {
986+
// Check if callbacks are registered before attempting fetch
987+
if (!isRegistered) {
988+
CppBridgePlatformAdapter.logCallback(
989+
CppBridgePlatformAdapter.LogLevel.ERROR,
990+
TAG,
991+
"❌ Cannot fetch model assignments: callbacks not registered. " +
992+
"Call register() first before fetchModelAssignments().",
993+
)
994+
return "[]"
995+
}
996+
922997
return try {
998+
CppBridgePlatformAdapter.logCallback(
999+
CppBridgePlatformAdapter.LogLevel.INFO,
1000+
TAG,
1001+
">>> Fetching model assignments from backend (forceRefresh: $forceRefresh)...",
1002+
)
1003+
9231004
val result = com.runanywhere.sdk.native.bridge.RunAnywhereBridge
9241005
.racModelAssignmentFetch(forceRefresh)
1006+
9251007
CppBridgePlatformAdapter.logCallback(
9261008
CppBridgePlatformAdapter.LogLevel.INFO,
9271009
TAG,
928-
"Fetched model assignments: ${result.take(100)}...",
1010+
"<<< Fetched model assignments: ${result.take(200)}${if (result.length > 200) "..." else ""}",
9291011
)
9301012
result
9311013
} catch (e: Exception) {
9321014
CppBridgePlatformAdapter.logCallback(
9331015
CppBridgePlatformAdapter.LogLevel.ERROR,
9341016
TAG,
935-
"Failed to fetch model assignments: ${e.message}",
1017+
"Failed to fetch model assignments: ${e.message}",
9361018
)
9371019
"[]"
9381020
}

0 commit comments

Comments
 (0)