capacitor-native-agent 0.9.7 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/t6x/plugins/nativeagent/MemoryProviderImpl.kt +40 -17
- package/android/src/main/jniLibs/arm64-v8a/libnative_agent_ffi.so +0 -0
- package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64/libnative_agent_ffi.a +0 -0
- package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64-simulator/libnative_agent_ffi.a +0 -0
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ package com.t6x.plugins.nativeagent
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import java.util.Locale
|
|
5
5
|
import java.util.UUID
|
|
6
|
+
import java.util.concurrent.Executors
|
|
6
7
|
import kotlinx.coroutines.runBlocking
|
|
7
8
|
import org.json.JSONArray
|
|
8
9
|
import org.json.JSONObject
|
|
@@ -13,6 +14,15 @@ import uniffi.native_agent_ffi.MemoryProvider
|
|
|
13
14
|
class MemoryProviderImpl(private val context: Context) : MemoryProvider {
|
|
14
15
|
fun isAvailable(): Boolean = LanceDBBridge.getOrCreateHandle(context) != null
|
|
15
16
|
|
|
17
|
+
// UniFFI methods on LanceDbHandle are suspend → require runBlocking. The
|
|
18
|
+
// MemoryProvider callback is invoked by Rust on a JNA-managed native
|
|
19
|
+
// thread; running coroutines (and uniffiRustCallAsync's own JNA callbacks)
|
|
20
|
+
// on that thread races with JNA's thread-detach logic, producing
|
|
21
|
+
// SIGABRT "attempting to detach while still running code" on Android ART.
|
|
22
|
+
// Hop to a dedicated JVM-owned worker thread so the JNA callback thread
|
|
23
|
+
// only blocks on Future.get(), never executes coroutine continuations.
|
|
24
|
+
private fun <T> onWorker(block: () -> T): T = WORKER.submit(block).get()
|
|
25
|
+
|
|
16
26
|
override fun store(key: String, text: String, metadataJson: String?): String {
|
|
17
27
|
val handle = LanceDBBridge.getOrCreateHandle(context) ?: return unavailableJson()
|
|
18
28
|
val resolvedKey = key.ifBlank {
|
|
@@ -20,8 +30,10 @@ class MemoryProviderImpl(private val context: Context) : MemoryProvider {
|
|
|
20
30
|
}
|
|
21
31
|
val embedding = localHashEmbed(text, LanceDBBridge.EMBEDDING_DIM)
|
|
22
32
|
return runCatching {
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
onWorker {
|
|
34
|
+
runBlocking {
|
|
35
|
+
handle.store(resolvedKey, DEFAULT_AGENT_ID, text, embedding, metadataJson)
|
|
36
|
+
}
|
|
25
37
|
}
|
|
26
38
|
JSONObject()
|
|
27
39
|
.put("success", true)
|
|
@@ -34,15 +46,17 @@ class MemoryProviderImpl(private val context: Context) : MemoryProvider {
|
|
|
34
46
|
val handle = LanceDBBridge.getOrCreateHandle(context) ?: return unavailableJson()
|
|
35
47
|
val embedding = localHashEmbed(query, LanceDBBridge.EMBEDDING_DIM)
|
|
36
48
|
return runCatching {
|
|
37
|
-
val results =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
val results = onWorker {
|
|
50
|
+
runBlocking {
|
|
51
|
+
handle.hybridSearch(
|
|
52
|
+
embedding,
|
|
53
|
+
query,
|
|
54
|
+
limit,
|
|
55
|
+
"agent_id = '$DEFAULT_AGENT_ID'",
|
|
56
|
+
null,
|
|
57
|
+
null,
|
|
58
|
+
)
|
|
59
|
+
}
|
|
46
60
|
}
|
|
47
61
|
hybridResultsJson(results)
|
|
48
62
|
}.getOrElse { errorJson(it) }
|
|
@@ -54,8 +68,10 @@ class MemoryProviderImpl(private val context: Context) : MemoryProvider {
|
|
|
54
68
|
return JSONObject().put("error", "Provide query or key.").toString()
|
|
55
69
|
}
|
|
56
70
|
return runCatching {
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
onWorker {
|
|
72
|
+
runBlocking {
|
|
73
|
+
handle.delete(key)
|
|
74
|
+
}
|
|
59
75
|
}
|
|
60
76
|
JSONObject()
|
|
61
77
|
.put("success", true)
|
|
@@ -68,8 +84,10 @@ class MemoryProviderImpl(private val context: Context) : MemoryProvider {
|
|
|
68
84
|
val handle = LanceDBBridge.getOrCreateHandle(context) ?: return unavailableJson()
|
|
69
85
|
val embedding = localHashEmbed(query, LanceDBBridge.EMBEDDING_DIM)
|
|
70
86
|
return runCatching {
|
|
71
|
-
val results =
|
|
72
|
-
|
|
87
|
+
val results = onWorker {
|
|
88
|
+
runBlocking {
|
|
89
|
+
handle.search(embedding, maxResults, "agent_id = '$DEFAULT_AGENT_ID'")
|
|
90
|
+
}
|
|
73
91
|
}
|
|
74
92
|
searchResultsJson(results)
|
|
75
93
|
}.getOrElse { errorJson(it) }
|
|
@@ -78,8 +96,10 @@ class MemoryProviderImpl(private val context: Context) : MemoryProvider {
|
|
|
78
96
|
override fun list(prefix: String?, limit: UInt?): String {
|
|
79
97
|
val handle = LanceDBBridge.getOrCreateHandle(context) ?: return unavailableJson()
|
|
80
98
|
return runCatching {
|
|
81
|
-
val keys =
|
|
82
|
-
|
|
99
|
+
val keys = onWorker {
|
|
100
|
+
runBlocking {
|
|
101
|
+
handle.list(prefix, limit)
|
|
102
|
+
}
|
|
83
103
|
}
|
|
84
104
|
JSONArray(keys).toString()
|
|
85
105
|
}.getOrElse { errorJson(it) }
|
|
@@ -131,6 +151,9 @@ class MemoryProviderImpl(private val context: Context) : MemoryProvider {
|
|
|
131
151
|
|
|
132
152
|
private companion object {
|
|
133
153
|
private const val DEFAULT_AGENT_ID = "main"
|
|
154
|
+
private val WORKER = Executors.newSingleThreadExecutor { r ->
|
|
155
|
+
Thread(r, "memory-provider-worker").apply { isDaemon = true }
|
|
156
|
+
}
|
|
134
157
|
private val WHITESPACE = Regex("\\s+")
|
|
135
158
|
private val UINT_MAX_DOUBLE = 0xffffffffu.toDouble()
|
|
136
159
|
private const val FNV_OFFSET = 0x811c9dc5u
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "capacitor-native-agent",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"description": "Native AI agent loop for Capacitor — runs LLM completions, tool execution, and cron jobs in native Rust, enabling background execution.",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/esm/index.d.ts",
|