@dvai-bridge/capacitor-llama 4.0.0 → 4.0.1
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/DVAICapacitorLlama.podspec +21 -21
- package/LICENSE +341 -34
- package/android/build.gradle +91 -91
- package/android/gradle.properties +4 -4
- package/android/settings.gradle +4 -4
- package/android/src/androidTest/java/co/deepvoiceai/bridge/llama/RealModelSmokeTest.kt +238 -238
- package/android/src/main/AndroidManifest.xml +7 -7
- package/android/src/main/java/co/deepvoiceai/bridge/llama/Plugin.kt +177 -177
- package/android/src/main/res/xml/dvai_network_security_config.xml +7 -7
- package/android/src/test/java/co/deepvoiceai/bridge/llama/SmokeTest.kt +11 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/ios/Package.swift +62 -57
- package/ios/Sources/DVAICapacitorLlama/Plugin.swift +154 -154
- package/ios/Sources/DVAICapacitorLlama/PluginProxy.m +15 -15
- package/ios/Tests/DVAICapacitorLlamaTests/RealModelSmokeTest.swift +429 -412
- package/ios/Tests/DVAICapacitorLlamaTests/SmokeTest.swift +15 -10
- package/package.json +6 -6
- package/README.md +0 -199
|
@@ -1,177 +1,177 @@
|
|
|
1
|
-
package co.deepvoiceai.bridge.llama
|
|
2
|
-
|
|
3
|
-
import com.getcapacitor.JSArray
|
|
4
|
-
import com.getcapacitor.JSObject
|
|
5
|
-
import com.getcapacitor.Plugin
|
|
6
|
-
import com.getcapacitor.PluginCall
|
|
7
|
-
import com.getcapacitor.PluginMethod
|
|
8
|
-
import com.getcapacitor.annotation.CapacitorPlugin
|
|
9
|
-
import co.deepvoiceai.bridge.llama.core.ModelDownloader
|
|
10
|
-
import co.deepvoiceai.bridge.llama.core.PluginState
|
|
11
|
-
import kotlinx.coroutines.CoroutineScope
|
|
12
|
-
import kotlinx.coroutines.Dispatchers
|
|
13
|
-
import kotlinx.coroutines.SupervisorJob
|
|
14
|
-
import kotlinx.coroutines.cancel
|
|
15
|
-
import kotlinx.coroutines.launch
|
|
16
|
-
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
// JSObject ↔ Map translation helpers — needed because PluginState's API is
|
|
19
|
-
// Capacitor-neutral (Map<String, Any?>) while the JS-bridge layer works with
|
|
20
|
-
// Capacitor's JSObject (which extends JSONObject and has no .toMap()).
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
|
|
23
|
-
private fun JSObject.toAnyMap(): Map<String, Any?> {
|
|
24
|
-
val out = mutableMapOf<String, Any?>()
|
|
25
|
-
val it = keys()
|
|
26
|
-
while (it.hasNext()) {
|
|
27
|
-
val k = it.next()
|
|
28
|
-
out[k] = this.opt(k) // returns Any (or JSONObject.NULL); fine for our usage
|
|
29
|
-
}
|
|
30
|
-
return out
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
private fun Map<String, Any?>.toJSObject(): JSObject {
|
|
34
|
-
val obj = JSObject()
|
|
35
|
-
for ((k, v) in this) {
|
|
36
|
-
if (v != null) obj.put(k, v)
|
|
37
|
-
}
|
|
38
|
-
return obj
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
@CapacitorPlugin(name = "DVAIBridgeLlama")
|
|
42
|
-
class DVAIBridgeLlamaPlugin : Plugin() {
|
|
43
|
-
private val state = PluginState()
|
|
44
|
-
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
|
45
|
-
private val downloader: ModelDownloader by lazy { ModelDownloader(context) }
|
|
46
|
-
|
|
47
|
-
override fun handleOnDestroy() {
|
|
48
|
-
super.handleOnDestroy()
|
|
49
|
-
scope.cancel()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
@PluginMethod
|
|
53
|
-
fun start(call: PluginCall) {
|
|
54
|
-
scope.launch {
|
|
55
|
-
notifyListeners("progress", JSObject().apply { put("phase", "load") })
|
|
56
|
-
try {
|
|
57
|
-
val resultMap = state.start((call.data ?: JSObject()).toAnyMap())
|
|
58
|
-
notifyListeners("progress", JSObject().apply { put("phase", "ready") })
|
|
59
|
-
call.resolve(resultMap.toJSObject())
|
|
60
|
-
} catch (e: Exception) {
|
|
61
|
-
notifyListeners("progress", JSObject().apply {
|
|
62
|
-
put("phase", "error")
|
|
63
|
-
put("message", e.message ?: "Start failed")
|
|
64
|
-
})
|
|
65
|
-
call.reject(e.message ?: "Start failed", e)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
@PluginMethod
|
|
71
|
-
fun stop(call: PluginCall) {
|
|
72
|
-
scope.launch {
|
|
73
|
-
try {
|
|
74
|
-
state.stop()
|
|
75
|
-
call.resolve()
|
|
76
|
-
} catch (e: Exception) {
|
|
77
|
-
call.reject(e.message ?: "Stop failed", e)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
@PluginMethod
|
|
83
|
-
fun status(call: PluginCall) {
|
|
84
|
-
scope.launch {
|
|
85
|
-
call.resolve(state.statusInfo().toJSObject())
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
@PluginMethod
|
|
90
|
-
fun downloadModel(call: PluginCall) {
|
|
91
|
-
val url = call.getString("url") ?: return call.reject("url is required")
|
|
92
|
-
val sha = call.getString("sha256")?.takeIf { it.isNotEmpty() }?.lowercase()
|
|
93
|
-
?: return call.reject("sha256 is required")
|
|
94
|
-
val destFilename = call.getString("destFilename") ?: url.substringAfterLast('/')
|
|
95
|
-
val headers: Map<String, String> = call.getObject("headers")?.let { obj ->
|
|
96
|
-
val map = mutableMapOf<String, String>()
|
|
97
|
-
obj.keys().forEachRemaining { k -> obj.getString(k)?.let { map[k] = it } }
|
|
98
|
-
map
|
|
99
|
-
} ?: emptyMap()
|
|
100
|
-
|
|
101
|
-
scope.launch {
|
|
102
|
-
try {
|
|
103
|
-
val (path, cached) = downloader.downloadModel(
|
|
104
|
-
url = url,
|
|
105
|
-
expectedSha256 = sha,
|
|
106
|
-
destFilename = destFilename,
|
|
107
|
-
headers = headers,
|
|
108
|
-
) { bytesDone, bytesTotal ->
|
|
109
|
-
val payload = JSObject().apply {
|
|
110
|
-
put("phase", "download")
|
|
111
|
-
put("bytesReceived", bytesDone)
|
|
112
|
-
if (bytesTotal != null) {
|
|
113
|
-
put("bytesTotal", bytesTotal)
|
|
114
|
-
if (bytesTotal > 0) {
|
|
115
|
-
put("percent", bytesDone.toDouble() / bytesTotal.toDouble() * 100.0)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
notifyListeners("progress", payload)
|
|
120
|
-
}
|
|
121
|
-
call.resolve(JSObject().apply {
|
|
122
|
-
put("path", path)
|
|
123
|
-
put("cached", cached)
|
|
124
|
-
})
|
|
125
|
-
} catch (e: Exception) {
|
|
126
|
-
call.reject(e.message ?: "Download failed", e)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
@PluginMethod
|
|
132
|
-
fun listCachedModels(call: PluginCall) {
|
|
133
|
-
scope.launch {
|
|
134
|
-
try {
|
|
135
|
-
val infos = downloader.listCached()
|
|
136
|
-
val arr = JSArray()
|
|
137
|
-
infos.forEach { info ->
|
|
138
|
-
arr.put(JSObject().apply {
|
|
139
|
-
put("filename", info.filename)
|
|
140
|
-
put("path", info.path)
|
|
141
|
-
put("bytes", info.bytes)
|
|
142
|
-
put("sha256", info.sha256)
|
|
143
|
-
})
|
|
144
|
-
}
|
|
145
|
-
call.resolve(JSObject().apply { put("models", arr) })
|
|
146
|
-
} catch (e: Exception) {
|
|
147
|
-
call.reject(e.message ?: "List failed", e)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
@PluginMethod
|
|
153
|
-
fun deleteCachedModel(call: PluginCall) {
|
|
154
|
-
val filename = call.getString("filename")?.takeIf { it.isNotEmpty() }
|
|
155
|
-
?: return call.reject("filename is required")
|
|
156
|
-
scope.launch {
|
|
157
|
-
try {
|
|
158
|
-
downloader.deleteCached(filename)
|
|
159
|
-
call.resolve()
|
|
160
|
-
} catch (e: Exception) {
|
|
161
|
-
call.reject(e.message ?: "Delete failed", e)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
@PluginMethod
|
|
167
|
-
fun cacheDir(call: PluginCall) {
|
|
168
|
-
scope.launch {
|
|
169
|
-
try {
|
|
170
|
-
val path = downloader.cacheDir().absolutePath
|
|
171
|
-
call.resolve(JSObject().apply { put("path", path) })
|
|
172
|
-
} catch (e: Exception) {
|
|
173
|
-
call.reject(e.message ?: "cacheDir failed", e)
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
1
|
+
package co.deepvoiceai.bridge.llama
|
|
2
|
+
|
|
3
|
+
import com.getcapacitor.JSArray
|
|
4
|
+
import com.getcapacitor.JSObject
|
|
5
|
+
import com.getcapacitor.Plugin
|
|
6
|
+
import com.getcapacitor.PluginCall
|
|
7
|
+
import com.getcapacitor.PluginMethod
|
|
8
|
+
import com.getcapacitor.annotation.CapacitorPlugin
|
|
9
|
+
import co.deepvoiceai.bridge.llama.core.ModelDownloader
|
|
10
|
+
import co.deepvoiceai.bridge.llama.core.PluginState
|
|
11
|
+
import kotlinx.coroutines.CoroutineScope
|
|
12
|
+
import kotlinx.coroutines.Dispatchers
|
|
13
|
+
import kotlinx.coroutines.SupervisorJob
|
|
14
|
+
import kotlinx.coroutines.cancel
|
|
15
|
+
import kotlinx.coroutines.launch
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// JSObject ↔ Map translation helpers — needed because PluginState's API is
|
|
19
|
+
// Capacitor-neutral (Map<String, Any?>) while the JS-bridge layer works with
|
|
20
|
+
// Capacitor's JSObject (which extends JSONObject and has no .toMap()).
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
private fun JSObject.toAnyMap(): Map<String, Any?> {
|
|
24
|
+
val out = mutableMapOf<String, Any?>()
|
|
25
|
+
val it = keys()
|
|
26
|
+
while (it.hasNext()) {
|
|
27
|
+
val k = it.next()
|
|
28
|
+
out[k] = this.opt(k) // returns Any (or JSONObject.NULL); fine for our usage
|
|
29
|
+
}
|
|
30
|
+
return out
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private fun Map<String, Any?>.toJSObject(): JSObject {
|
|
34
|
+
val obj = JSObject()
|
|
35
|
+
for ((k, v) in this) {
|
|
36
|
+
if (v != null) obj.put(k, v)
|
|
37
|
+
}
|
|
38
|
+
return obj
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@CapacitorPlugin(name = "DVAIBridgeLlama")
|
|
42
|
+
class DVAIBridgeLlamaPlugin : Plugin() {
|
|
43
|
+
private val state = PluginState()
|
|
44
|
+
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
|
45
|
+
private val downloader: ModelDownloader by lazy { ModelDownloader(context) }
|
|
46
|
+
|
|
47
|
+
override fun handleOnDestroy() {
|
|
48
|
+
super.handleOnDestroy()
|
|
49
|
+
scope.cancel()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@PluginMethod
|
|
53
|
+
fun start(call: PluginCall) {
|
|
54
|
+
scope.launch {
|
|
55
|
+
notifyListeners("progress", JSObject().apply { put("phase", "load") })
|
|
56
|
+
try {
|
|
57
|
+
val resultMap = state.start((call.data ?: JSObject()).toAnyMap())
|
|
58
|
+
notifyListeners("progress", JSObject().apply { put("phase", "ready") })
|
|
59
|
+
call.resolve(resultMap.toJSObject())
|
|
60
|
+
} catch (e: Exception) {
|
|
61
|
+
notifyListeners("progress", JSObject().apply {
|
|
62
|
+
put("phase", "error")
|
|
63
|
+
put("message", e.message ?: "Start failed")
|
|
64
|
+
})
|
|
65
|
+
call.reject(e.message ?: "Start failed", e)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@PluginMethod
|
|
71
|
+
fun stop(call: PluginCall) {
|
|
72
|
+
scope.launch {
|
|
73
|
+
try {
|
|
74
|
+
state.stop()
|
|
75
|
+
call.resolve()
|
|
76
|
+
} catch (e: Exception) {
|
|
77
|
+
call.reject(e.message ?: "Stop failed", e)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@PluginMethod
|
|
83
|
+
fun status(call: PluginCall) {
|
|
84
|
+
scope.launch {
|
|
85
|
+
call.resolve(state.statusInfo().toJSObject())
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@PluginMethod
|
|
90
|
+
fun downloadModel(call: PluginCall) {
|
|
91
|
+
val url = call.getString("url") ?: return call.reject("url is required")
|
|
92
|
+
val sha = call.getString("sha256")?.takeIf { it.isNotEmpty() }?.lowercase()
|
|
93
|
+
?: return call.reject("sha256 is required")
|
|
94
|
+
val destFilename = call.getString("destFilename") ?: url.substringAfterLast('/')
|
|
95
|
+
val headers: Map<String, String> = call.getObject("headers")?.let { obj ->
|
|
96
|
+
val map = mutableMapOf<String, String>()
|
|
97
|
+
obj.keys().forEachRemaining { k -> obj.getString(k)?.let { map[k] = it } }
|
|
98
|
+
map
|
|
99
|
+
} ?: emptyMap()
|
|
100
|
+
|
|
101
|
+
scope.launch {
|
|
102
|
+
try {
|
|
103
|
+
val (path, cached) = downloader.downloadModel(
|
|
104
|
+
url = url,
|
|
105
|
+
expectedSha256 = sha,
|
|
106
|
+
destFilename = destFilename,
|
|
107
|
+
headers = headers,
|
|
108
|
+
) { bytesDone, bytesTotal ->
|
|
109
|
+
val payload = JSObject().apply {
|
|
110
|
+
put("phase", "download")
|
|
111
|
+
put("bytesReceived", bytesDone)
|
|
112
|
+
if (bytesTotal != null) {
|
|
113
|
+
put("bytesTotal", bytesTotal)
|
|
114
|
+
if (bytesTotal > 0) {
|
|
115
|
+
put("percent", bytesDone.toDouble() / bytesTotal.toDouble() * 100.0)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
notifyListeners("progress", payload)
|
|
120
|
+
}
|
|
121
|
+
call.resolve(JSObject().apply {
|
|
122
|
+
put("path", path)
|
|
123
|
+
put("cached", cached)
|
|
124
|
+
})
|
|
125
|
+
} catch (e: Exception) {
|
|
126
|
+
call.reject(e.message ?: "Download failed", e)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@PluginMethod
|
|
132
|
+
fun listCachedModels(call: PluginCall) {
|
|
133
|
+
scope.launch {
|
|
134
|
+
try {
|
|
135
|
+
val infos = downloader.listCached()
|
|
136
|
+
val arr = JSArray()
|
|
137
|
+
infos.forEach { info ->
|
|
138
|
+
arr.put(JSObject().apply {
|
|
139
|
+
put("filename", info.filename)
|
|
140
|
+
put("path", info.path)
|
|
141
|
+
put("bytes", info.bytes)
|
|
142
|
+
put("sha256", info.sha256)
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
call.resolve(JSObject().apply { put("models", arr) })
|
|
146
|
+
} catch (e: Exception) {
|
|
147
|
+
call.reject(e.message ?: "List failed", e)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@PluginMethod
|
|
153
|
+
fun deleteCachedModel(call: PluginCall) {
|
|
154
|
+
val filename = call.getString("filename")?.takeIf { it.isNotEmpty() }
|
|
155
|
+
?: return call.reject("filename is required")
|
|
156
|
+
scope.launch {
|
|
157
|
+
try {
|
|
158
|
+
downloader.deleteCached(filename)
|
|
159
|
+
call.resolve()
|
|
160
|
+
} catch (e: Exception) {
|
|
161
|
+
call.reject(e.message ?: "Delete failed", e)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@PluginMethod
|
|
167
|
+
fun cacheDir(call: PluginCall) {
|
|
168
|
+
scope.launch {
|
|
169
|
+
try {
|
|
170
|
+
val path = downloader.cacheDir().absolutePath
|
|
171
|
+
call.resolve(JSObject().apply { put("path", path) })
|
|
172
|
+
} catch (e: Exception) {
|
|
173
|
+
call.reject(e.message ?: "cacheDir failed", e)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
-
<network-security-config>
|
|
3
|
-
<domain-config cleartextTrafficPermitted="true">
|
|
4
|
-
<domain includeSubdomains="false">localhost</domain>
|
|
5
|
-
<domain includeSubdomains="false">127.0.0.1</domain>
|
|
6
|
-
</domain-config>
|
|
7
|
-
</network-security-config>
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<network-security-config>
|
|
3
|
+
<domain-config cleartextTrafficPermitted="true">
|
|
4
|
+
<domain includeSubdomains="false">localhost</domain>
|
|
5
|
+
<domain includeSubdomains="false">127.0.0.1</domain>
|
|
6
|
+
</domain-config>
|
|
7
|
+
</network-security-config>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
package co.deepvoiceai.bridge.llama
|
|
2
|
-
|
|
3
|
-
import org.junit.Assert.assertNotNull
|
|
4
|
-
import org.junit.Test
|
|
5
|
-
|
|
6
|
-
class SmokeTest {
|
|
7
|
-
@Test
|
|
8
|
-
fun pluginClassExists() {
|
|
9
|
-
assertNotNull(DVAIBridgeLlamaPlugin::class.java)
|
|
10
|
-
}
|
|
11
|
-
}
|
|
1
|
+
package co.deepvoiceai.bridge.llama
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.assertNotNull
|
|
4
|
+
import org.junit.Test
|
|
5
|
+
|
|
6
|
+
class SmokeTest {
|
|
7
|
+
@Test
|
|
8
|
+
fun pluginClassExists() {
|
|
9
|
+
assertNotNull(DVAIBridgeLlamaPlugin::class.java)
|
|
10
|
+
}
|
|
11
|
+
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nimport type { NativePluginInterface } from \"@dvai-bridge/capacitor\";\n\nconst DVAIBridgeLlama = registerPlugin<NativePluginInterface>(\"DVAIBridgeLlama\");\n\nexport default DVAIBridgeLlama;\nexport { DVAIBridgeLlama };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA+B;AAG/B,IAAM,sBAAkB,4BAAsC,iBAAiB;AAE/E,IAAO,gBAAQ;","names":[]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nimport type { NativePluginInterface } from \"@dvai-bridge/capacitor\";\n\nconst DVAIBridgeLlama = registerPlugin<NativePluginInterface>(\"DVAIBridgeLlama\");\n\nexport default DVAIBridgeLlama;\nexport { DVAIBridgeLlama };\n"],"mappings":";AAAA,SAAS,sBAAsB;AAG/B,IAAM,kBAAkB,eAAsC,iBAAiB;AAE/E,IAAO,gBAAQ;","names":[]}
|
package/ios/Package.swift
CHANGED
|
@@ -1,57 +1,62 @@
|
|
|
1
|
-
// swift-tools-version: 5.9
|
|
2
|
-
import PackageDescription
|
|
3
|
-
|
|
4
|
-
let package = Package(
|
|
5
|
-
name: "DVAICapacitorLlama",
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
.product(name: "
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
)
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "DVAICapacitorLlama",
|
|
6
|
+
// iOS 17 / macOS 14 — must match dvai-bridge-ios-llama-core
|
|
7
|
+
// (`platforms: [.iOS(.v17), .macOS(.v14)]`), which we depend on
|
|
8
|
+
// transitively via `DVAILlamaCore`/`DVAILlamaCoreObjC` below.
|
|
9
|
+
// Earlier .v14 / .v12 declaration was a vestige from before the
|
|
10
|
+
// Hummingbird migration (v3.2.0) bumped the shared-core floor.
|
|
11
|
+
platforms: [.iOS(.v17), .macOS(.v14)],
|
|
12
|
+
products: [
|
|
13
|
+
.library(name: "DVAICapacitorLlama", targets: ["DVAICapacitorLlama"]),
|
|
14
|
+
],
|
|
15
|
+
dependencies: [
|
|
16
|
+
// Capacitor SPM artifact (existing)
|
|
17
|
+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm", branch: "main"),
|
|
18
|
+
// Core package — relative path during dev, replaced with version-pin
|
|
19
|
+
// or git URL at publish time. The path is relative to *this* Package.swift's
|
|
20
|
+
// location (`packages/dvai-bridge-capacitor-llama/ios/`), so two `..` get
|
|
21
|
+
// us to the `packages/` parent and `dvai-bridge-ios-llama-core/ios` is the
|
|
22
|
+
// sibling package's SPM root.
|
|
23
|
+
//
|
|
24
|
+
// The core's Package.swift lives at the PACKAGE ROOT (not under `ios/`)
|
|
25
|
+
// so that SPM derives identity "dvai-bridge-ios-llama-core" rather than
|
|
26
|
+
// "ios" for this dependency. With both Package.swifts at `ios/`, SPM's
|
|
27
|
+
// path-dep identity-from-last-dir-name rule aliased them and triggered
|
|
28
|
+
// a false `cyclic dependency between packages DVAICapacitorLlama ->
|
|
29
|
+
// DVAICapacitorLlama` resolution error. The bare product reference
|
|
30
|
+
// ("DVAILlamaCore") in the target dependency list below works because
|
|
31
|
+
// SPM auto-resolves unambiguous product names across the dep graph.
|
|
32
|
+
.package(path: "../../dvai-bridge-ios-llama-core"),
|
|
33
|
+
],
|
|
34
|
+
targets: [
|
|
35
|
+
.target(
|
|
36
|
+
name: "DVAICapacitorLlama",
|
|
37
|
+
dependencies: [
|
|
38
|
+
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
39
|
+
.product(name: "Cordova", package: "capacitor-swift-pm"),
|
|
40
|
+
// The package's identity is derived from the last dir of the
|
|
41
|
+
// path dep (`dvai-bridge-ios-llama-core`), not from the
|
|
42
|
+
// manifest's `name:` field. Bare-name product references don't
|
|
43
|
+
// cross package boundaries reliably; explicit form is required.
|
|
44
|
+
.product(name: "DVAILlamaCore", package: "dvai-bridge-ios-llama-core"),
|
|
45
|
+
],
|
|
46
|
+
path: "Sources/DVAICapacitorLlama",
|
|
47
|
+
exclude: ["PluginProxy.m"]
|
|
48
|
+
),
|
|
49
|
+
.testTarget(
|
|
50
|
+
name: "DVAICapacitorLlamaTests",
|
|
51
|
+
dependencies: [
|
|
52
|
+
"DVAICapacitorLlama",
|
|
53
|
+
// RealModelSmokeTest reaches the core directly (LlamaCppBridge,
|
|
54
|
+
// ModelDownloader, MTMD_MEDIA_MARKER) — declare both core products
|
|
55
|
+
// explicitly so Xcode resolves them at link time.
|
|
56
|
+
.product(name: "DVAILlamaCore", package: "dvai-bridge-ios-llama-core"),
|
|
57
|
+
.product(name: "DVAILlamaCoreObjC", package: "dvai-bridge-ios-llama-core"),
|
|
58
|
+
],
|
|
59
|
+
path: "Tests/DVAICapacitorLlamaTests"
|
|
60
|
+
),
|
|
61
|
+
]
|
|
62
|
+
)
|