llama-cpp-pro 0.2.2

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.
Files changed (310) hide show
  1. package/CHANGELOG.md +295 -0
  2. package/LICENSE +21 -0
  3. package/LlamaCpp.podspec +33 -0
  4. package/LlamaCppCapacitor.podspec +33 -0
  5. package/Package.swift +29 -0
  6. package/README.md +93 -0
  7. package/android/build.gradle +88 -0
  8. package/android/src/main/AndroidManifest.xml +4 -0
  9. package/android/src/main/CMakeLists-arm64.txt +138 -0
  10. package/android/src/main/CMakeLists-x86_64.txt +141 -0
  11. package/android/src/main/CMakeLists.txt +138 -0
  12. package/android/src/main/java/ai/annadata/plugin/capacitor/LlamaCpp.java +1340 -0
  13. package/android/src/main/java/ai/annadata/plugin/capacitor/LlamaCppPlugin.java +814 -0
  14. package/android/src/main/java/ai/annadata/plugin/capacitor/ModelAdmissionController.java +214 -0
  15. package/android/src/main/jni-chat-session.cpp +261 -0
  16. package/android/src/main/jni-lora.cpp +197 -0
  17. package/android/src/main/jni-multimodal.cpp +167 -0
  18. package/android/src/main/jni-tts.cpp +290 -0
  19. package/android/src/main/jni-utils.h +148 -0
  20. package/android/src/main/jni.cpp +1808 -0
  21. package/android/src/main/jniLibs/arm64-v8a/libllama-cpp-arm64.so +0 -0
  22. package/android/src/main/res/.gitkeep +0 -0
  23. package/build-native.sh +280 -0
  24. package/cmake/desktop-metal-embed.cmake +30 -0
  25. package/cmake/desktop-sources.cmake +100 -0
  26. package/cmake/ggml-backends.cmake +44 -0
  27. package/cpp/LICENSE +21 -0
  28. package/cpp/README.md +15 -0
  29. package/cpp/anyascii.c +22223 -0
  30. package/cpp/anyascii.h +42 -0
  31. package/cpp/cap-completion.cpp +942 -0
  32. package/cpp/cap-completion.h +127 -0
  33. package/cpp/cap-embedding.cpp +193 -0
  34. package/cpp/cap-embedding.h +35 -0
  35. package/cpp/cap-ios-bridge.cpp +1810 -0
  36. package/cpp/cap-ios-bridge.h +61 -0
  37. package/cpp/cap-llama.cpp +412 -0
  38. package/cpp/cap-llama.h +161 -0
  39. package/cpp/cap-mtmd.hpp +602 -0
  40. package/cpp/cap-native-server.cpp +1095 -0
  41. package/cpp/cap-native-server.h +40 -0
  42. package/cpp/cap-tts.cpp +591 -0
  43. package/cpp/cap-tts.h +59 -0
  44. package/cpp/cap-wasm-fs.cpp +156 -0
  45. package/cpp/cap-wasm-jspi.cpp +19 -0
  46. package/cpp/cap-wasm-jspi.h +30 -0
  47. package/cpp/cap-wasm-vfs.cpp +22 -0
  48. package/cpp/chat-parser.cpp +393 -0
  49. package/cpp/chat-parser.h +120 -0
  50. package/cpp/chat.cpp +2315 -0
  51. package/cpp/chat.h +221 -0
  52. package/cpp/common.cpp +1664 -0
  53. package/cpp/common.h +744 -0
  54. package/cpp/ggml-alloc.c +1028 -0
  55. package/cpp/ggml-alloc.h +76 -0
  56. package/cpp/ggml-backend-impl.h +255 -0
  57. package/cpp/ggml-backend-reg.cpp +600 -0
  58. package/cpp/ggml-backend.cpp +2121 -0
  59. package/cpp/ggml-backend.h +354 -0
  60. package/cpp/ggml-common.h +1878 -0
  61. package/cpp/ggml-cpp.h +39 -0
  62. package/cpp/ggml-cpu/amx/amx.cpp +221 -0
  63. package/cpp/ggml-cpu/amx/amx.h +8 -0
  64. package/cpp/ggml-cpu/amx/common.h +91 -0
  65. package/cpp/ggml-cpu/amx/mmq.cpp +2512 -0
  66. package/cpp/ggml-cpu/amx/mmq.h +10 -0
  67. package/cpp/ggml-cpu/arch/arm/cpu-feats.cpp +94 -0
  68. package/cpp/ggml-cpu/arch/arm/quants.c +3650 -0
  69. package/cpp/ggml-cpu/arch/arm/repack.cpp +1891 -0
  70. package/cpp/ggml-cpu/arch/x86/cpu-feats.cpp +327 -0
  71. package/cpp/ggml-cpu/arch/x86/quants.c +3820 -0
  72. package/cpp/ggml-cpu/arch/x86/repack.cpp +6307 -0
  73. package/cpp/ggml-cpu/arch-fallback.h +215 -0
  74. package/cpp/ggml-cpu/binary-ops.cpp +158 -0
  75. package/cpp/ggml-cpu/binary-ops.h +16 -0
  76. package/cpp/ggml-cpu/common.h +73 -0
  77. package/cpp/ggml-cpu/ggml-cpu-impl.h +559 -0
  78. package/cpp/ggml-cpu/ggml-cpu.c +3578 -0
  79. package/cpp/ggml-cpu/ggml-cpu.cpp +672 -0
  80. package/cpp/ggml-cpu/ops.cpp +10587 -0
  81. package/cpp/ggml-cpu/ops.h +114 -0
  82. package/cpp/ggml-cpu/quants.c +1193 -0
  83. package/cpp/ggml-cpu/quants.h +97 -0
  84. package/cpp/ggml-cpu/repack.cpp +1982 -0
  85. package/cpp/ggml-cpu/repack.h +120 -0
  86. package/cpp/ggml-cpu/simd-mappings.h +1184 -0
  87. package/cpp/ggml-cpu/traits.cpp +36 -0
  88. package/cpp/ggml-cpu/traits.h +38 -0
  89. package/cpp/ggml-cpu/unary-ops.cpp +186 -0
  90. package/cpp/ggml-cpu/unary-ops.h +28 -0
  91. package/cpp/ggml-cpu/vec.cpp +348 -0
  92. package/cpp/ggml-cpu/vec.h +1121 -0
  93. package/cpp/ggml-cpu.h +145 -0
  94. package/cpp/ggml-impl.h +622 -0
  95. package/cpp/ggml-metal-impl.h +688 -0
  96. package/cpp/ggml-metal.h +66 -0
  97. package/cpp/ggml-metal.m +6833 -0
  98. package/cpp/ggml-metal.metal +10754 -0
  99. package/cpp/ggml-opt.cpp +1093 -0
  100. package/cpp/ggml-opt.h +256 -0
  101. package/cpp/ggml-quants.c +5324 -0
  102. package/cpp/ggml-quants.h +106 -0
  103. package/cpp/ggml-threading.cpp +12 -0
  104. package/cpp/ggml-threading.h +14 -0
  105. package/cpp/ggml.c +7108 -0
  106. package/cpp/ggml.h +2492 -0
  107. package/cpp/gguf.cpp +1358 -0
  108. package/cpp/gguf.h +202 -0
  109. package/cpp/json-partial.cpp +256 -0
  110. package/cpp/json-partial.h +38 -0
  111. package/cpp/json-schema-to-grammar.cpp +985 -0
  112. package/cpp/json-schema-to-grammar.h +21 -0
  113. package/cpp/llama-adapter.cpp +388 -0
  114. package/cpp/llama-adapter.h +76 -0
  115. package/cpp/llama-arch.cpp +2355 -0
  116. package/cpp/llama-arch.h +499 -0
  117. package/cpp/llama-batch.cpp +875 -0
  118. package/cpp/llama-batch.h +160 -0
  119. package/cpp/llama-chat.cpp +783 -0
  120. package/cpp/llama-chat.h +65 -0
  121. package/cpp/llama-context.cpp +2788 -0
  122. package/cpp/llama-context.h +306 -0
  123. package/cpp/llama-cparams.cpp +5 -0
  124. package/cpp/llama-cparams.h +41 -0
  125. package/cpp/llama-cpp.h +30 -0
  126. package/cpp/llama-grammar.cpp +1229 -0
  127. package/cpp/llama-grammar.h +173 -0
  128. package/cpp/llama-graph.cpp +1891 -0
  129. package/cpp/llama-graph.h +810 -0
  130. package/cpp/llama-hparams.cpp +180 -0
  131. package/cpp/llama-hparams.h +233 -0
  132. package/cpp/llama-impl.cpp +167 -0
  133. package/cpp/llama-impl.h +61 -0
  134. package/cpp/llama-io.cpp +15 -0
  135. package/cpp/llama-io.h +35 -0
  136. package/cpp/llama-kv-cache-iswa.cpp +318 -0
  137. package/cpp/llama-kv-cache-iswa.h +135 -0
  138. package/cpp/llama-kv-cache.cpp +2059 -0
  139. package/cpp/llama-kv-cache.h +374 -0
  140. package/cpp/llama-kv-cells.h +491 -0
  141. package/cpp/llama-memory-hybrid.cpp +258 -0
  142. package/cpp/llama-memory-hybrid.h +137 -0
  143. package/cpp/llama-memory-recurrent.cpp +1146 -0
  144. package/cpp/llama-memory-recurrent.h +179 -0
  145. package/cpp/llama-memory.cpp +59 -0
  146. package/cpp/llama-memory.h +119 -0
  147. package/cpp/llama-mmap.cpp +609 -0
  148. package/cpp/llama-mmap.h +68 -0
  149. package/cpp/llama-model-loader.cpp +1166 -0
  150. package/cpp/llama-model-loader.h +170 -0
  151. package/cpp/llama-model-saver.cpp +282 -0
  152. package/cpp/llama-model-saver.h +37 -0
  153. package/cpp/llama-model.cpp +19061 -0
  154. package/cpp/llama-model.h +491 -0
  155. package/cpp/llama-sampling.cpp +2575 -0
  156. package/cpp/llama-sampling.h +32 -0
  157. package/cpp/llama-vocab.cpp +3792 -0
  158. package/cpp/llama-vocab.h +176 -0
  159. package/cpp/llama.cpp +358 -0
  160. package/cpp/llama.h +1373 -0
  161. package/cpp/log.cpp +428 -0
  162. package/cpp/log.h +103 -0
  163. package/cpp/minja/chat-template.hpp +550 -0
  164. package/cpp/minja/minja.hpp +3009 -0
  165. package/cpp/nlohmann/json.hpp +25526 -0
  166. package/cpp/nlohmann/json_fwd.hpp +187 -0
  167. package/cpp/regex-partial.cpp +204 -0
  168. package/cpp/regex-partial.h +56 -0
  169. package/cpp/sampling.cpp +579 -0
  170. package/cpp/sampling.h +107 -0
  171. package/cpp/tools/mtmd/clip-impl.h +473 -0
  172. package/cpp/tools/mtmd/clip.cpp +4322 -0
  173. package/cpp/tools/mtmd/clip.h +106 -0
  174. package/cpp/tools/mtmd/miniaudio/miniaudio.h +93468 -0
  175. package/cpp/tools/mtmd/mtmd-audio.cpp +769 -0
  176. package/cpp/tools/mtmd/mtmd-audio.h +47 -0
  177. package/cpp/tools/mtmd/mtmd-helper.cpp +460 -0
  178. package/cpp/tools/mtmd/mtmd-helper.h +95 -0
  179. package/cpp/tools/mtmd/mtmd.cpp +1066 -0
  180. package/cpp/tools/mtmd/mtmd.h +302 -0
  181. package/cpp/tools/mtmd/stb/stb_image.h +7988 -0
  182. package/cpp/unicode-data.cpp +7034 -0
  183. package/cpp/unicode-data.h +20 -0
  184. package/cpp/unicode.cpp +1061 -0
  185. package/cpp/unicode.h +68 -0
  186. package/cpp/vendor/cpp-httplib/httplib.cpp +16509 -0
  187. package/cpp/vendor/cpp-httplib/httplib.h +3883 -0
  188. package/desktop/electron-builder.config.cjs +157 -0
  189. package/desktop/entitlements.mac.plist +12 -0
  190. package/desktop/package.json +11 -0
  191. package/desktop/resolve-package-root.cjs +88 -0
  192. package/desktop/src/main/backend-selector.cjs +148 -0
  193. package/desktop/src/main/gpu-probe.cjs +142 -0
  194. package/desktop/src/main/index.cjs +58 -0
  195. package/desktop/src/main/ipc-handlers.cjs +212 -0
  196. package/desktop/src/main/model-store.cjs +50 -0
  197. package/desktop/src/main/preload.cjs +39 -0
  198. package/desktop/src/main/sidecar-client.cjs +76 -0
  199. package/desktop/src/main/sidecar-manager.cjs +364 -0
  200. package/dist/docs.json +14357 -0
  201. package/dist/esm/definitions.d.ts +731 -0
  202. package/dist/esm/definitions.js +2 -0
  203. package/dist/esm/definitions.js.map +1 -0
  204. package/dist/esm/desktop.d.ts +37 -0
  205. package/dist/esm/desktop.js +133 -0
  206. package/dist/esm/desktop.js.map +1 -0
  207. package/dist/esm/index.d.ts +200 -0
  208. package/dist/esm/index.js +612 -0
  209. package/dist/esm/index.js.map +1 -0
  210. package/dist/esm/isomorphic/desktop.runtime.d.ts +37 -0
  211. package/dist/esm/isomorphic/desktop.runtime.js +36 -0
  212. package/dist/esm/isomorphic/desktop.runtime.js.map +1 -0
  213. package/dist/esm/isomorphic/errors.d.ts +6 -0
  214. package/dist/esm/isomorphic/errors.js +9 -0
  215. package/dist/esm/isomorphic/errors.js.map +1 -0
  216. package/dist/esm/isomorphic/model.admission.d.ts +17 -0
  217. package/dist/esm/isomorphic/model.admission.js +27 -0
  218. package/dist/esm/isomorphic/model.admission.js.map +1 -0
  219. package/dist/esm/isomorphic/model.scheduler.d.ts +31 -0
  220. package/dist/esm/isomorphic/model.scheduler.js +95 -0
  221. package/dist/esm/isomorphic/model.scheduler.js.map +1 -0
  222. package/dist/esm/isomorphic/provider.desktop.d.ts +40 -0
  223. package/dist/esm/isomorphic/provider.desktop.js +411 -0
  224. package/dist/esm/isomorphic/provider.desktop.js.map +1 -0
  225. package/dist/esm/isomorphic/provider.factory.d.ts +2 -0
  226. package/dist/esm/isomorphic/provider.factory.js +16 -0
  227. package/dist/esm/isomorphic/provider.factory.js.map +1 -0
  228. package/dist/esm/isomorphic/provider.interface.d.ts +76 -0
  229. package/dist/esm/isomorphic/provider.interface.js +2 -0
  230. package/dist/esm/isomorphic/provider.interface.js.map +1 -0
  231. package/dist/esm/isomorphic/provider.native.d.ts +18 -0
  232. package/dist/esm/isomorphic/provider.native.js +173 -0
  233. package/dist/esm/isomorphic/provider.native.js.map +1 -0
  234. package/dist/esm/isomorphic/provider.web.d.ts +88 -0
  235. package/dist/esm/isomorphic/provider.web.js +573 -0
  236. package/dist/esm/isomorphic/provider.web.js.map +1 -0
  237. package/dist/esm/isomorphic/sidecar-sse.d.ts +14 -0
  238. package/dist/esm/isomorphic/sidecar-sse.js +76 -0
  239. package/dist/esm/isomorphic/sidecar-sse.js.map +1 -0
  240. package/dist/esm/isomorphic/wasmMemoryCalibration.d.ts +26 -0
  241. package/dist/esm/isomorphic/wasmMemoryCalibration.js +60 -0
  242. package/dist/esm/isomorphic/wasmMemoryCalibration.js.map +1 -0
  243. package/dist/esm/isomorphic/wasmMemoryPolicy.d.ts +55 -0
  244. package/dist/esm/isomorphic/wasmMemoryPolicy.js +93 -0
  245. package/dist/esm/isomorphic/wasmMemoryPolicy.js.map +1 -0
  246. package/dist/esm/storage/manifest.d.ts +13 -0
  247. package/dist/esm/storage/manifest.js +87 -0
  248. package/dist/esm/storage/manifest.js.map +1 -0
  249. package/dist/esm/storage/opfs.store.d.ts +31 -0
  250. package/dist/esm/storage/opfs.store.js +241 -0
  251. package/dist/esm/storage/opfs.store.js.map +1 -0
  252. package/dist/esm/web.d.ts +206 -0
  253. package/dist/esm/web.js +521 -0
  254. package/dist/esm/web.js.map +1 -0
  255. package/dist/esm/workers/async-file.d.ts +13 -0
  256. package/dist/esm/workers/async-file.js +12 -0
  257. package/dist/esm/workers/async-file.js.map +1 -0
  258. package/dist/esm/workers/heapfs.d.ts +55 -0
  259. package/dist/esm/workers/heapfs.js +115 -0
  260. package/dist/esm/workers/heapfs.js.map +1 -0
  261. package/dist/esm/workers/wasm.engine.d.ts +86 -0
  262. package/dist/esm/workers/wasm.engine.js +504 -0
  263. package/dist/esm/workers/wasm.engine.js.map +1 -0
  264. package/dist/esm/workers/worker.protocol.d.ts +160 -0
  265. package/dist/esm/workers/worker.protocol.js +2 -0
  266. package/dist/esm/workers/worker.protocol.js.map +1 -0
  267. package/dist/plugin.cjs +3179 -0
  268. package/dist/plugin.cjs.map +1 -0
  269. package/dist/plugin.js +3181 -0
  270. package/dist/plugin.js.map +1 -0
  271. package/dist/wasm/llama_engine.d.ts +74 -0
  272. package/dist/wasm/llama_engine.js +1829 -0
  273. package/dist/wasm/llama_engine.wasm +0 -0
  274. package/dist/wasm/llama_engine_emscripten.mjs +2 -0
  275. package/dist/wasm/package.json +16 -0
  276. package/dist/workers/llm.worker.js +1226 -0
  277. package/dist/workers/llm.worker.js.map +7 -0
  278. package/extraResources/llama-wasm/llama_engine.d.ts +74 -0
  279. package/extraResources/llama-wasm/llama_engine.js +1829 -0
  280. package/extraResources/llama-wasm/llama_engine.wasm +0 -0
  281. package/extraResources/llama-wasm/llama_engine_emscripten.mjs +2 -0
  282. package/extraResources/llama-wasm/package.json +16 -0
  283. package/extraResources/sidecar/README.md +11 -0
  284. package/extraResources/sidecar/darwin-arm64 +0 -0
  285. package/extraResources/sidecar/darwin-x64 +0 -0
  286. package/ios/CMakeLists-arm64.txt +161 -0
  287. package/ios/CMakeLists-x86_64.txt +188 -0
  288. package/ios/CMakeLists.txt +161 -0
  289. package/ios/Frameworks/llama-cpp.framework/Info.plist +28 -0
  290. package/ios/Frameworks/llama-cpp.framework/llama-cpp +0 -0
  291. package/ios/Sources/LlamaCppCapacitor/LlamaCpp.swift +1365 -0
  292. package/ios/Sources/LlamaCppCapacitor/LlamaCppPlugin.swift +694 -0
  293. package/ios/Sources/LlamaCppCapacitor/LlamaNativeBridge.swift +349 -0
  294. package/ios/Sources/LlamaCppCapacitor/ModelAdmissionController.swift +177 -0
  295. package/ios/embed-metal-shaders.sh +24 -0
  296. package/ios/metal-embed.cmake +29 -0
  297. package/package.json +281 -0
  298. package/scripts/build-js-preserve-wasm.cjs +53 -0
  299. package/scripts/build-sidecar-linux.sh +6 -0
  300. package/scripts/build-sidecar-win.bat +19 -0
  301. package/scripts/build-sidecar.sh +184 -0
  302. package/scripts/embed-llama-ios-app-framework.sh +63 -0
  303. package/scripts/ensure-desktop-sidecar-bundle.cjs +103 -0
  304. package/scripts/ensure-llama-ios-xcframework.sh +108 -0
  305. package/scripts/fix-esm-extensions.cjs +45 -0
  306. package/scripts/prepare-js-dist.cjs +28 -0
  307. package/scripts/stage-desktop-resources.cjs +116 -0
  308. package/sidecar/CMakeLists.txt +192 -0
  309. package/sidecar/cap-sidecar-main.cpp +68 -0
  310. package/types/llama-cpp-pro.d.ts +441 -0
@@ -0,0 +1,214 @@
1
+ package ai.annadata.plugin.capacitor;
2
+
3
+ import android.app.ActivityManager;
4
+ import android.content.Context;
5
+ import android.util.Log;
6
+ import java.io.File;
7
+ import java.util.HashMap;
8
+ import java.util.Map;
9
+
10
+ /**
11
+ * Memory admission control for native model loading on Android.
12
+ *
13
+ * <p>This mirrors the Web/WASM {@code DefaultModelScheduler} + {@code wasmMemoryPolicy}
14
+ * so that all three platforms share the same isomorphic admission semantics:
15
+ * <ul>
16
+ * <li>A hard cap on concurrent loaded models (parity with WASM_MAX_CONCURRENT_MODELS = 5).</li>
17
+ * <li>Per-model footprint estimation based on GGUF file size.</li>
18
+ * <li>A device-memory guard that keeps a reserve free after each load.</li>
19
+ * </ul>
20
+ *
21
+ * <p>Unlike the Web path (bounded by a single 2 GB WASM linear-memory pool), the native
22
+ * ceiling is the device's available RAM as reported by {@link ActivityManager}. The class
23
+ * therefore rejects a load when either the slot limit is reached or the projected free
24
+ * memory after loading would fall below the reserve threshold.
25
+ */
26
+ final class ModelAdmissionController {
27
+ private static final String TAG = "ModelAdmission";
28
+
29
+ /** Max concurrent native contexts. Parity with WASM_MAX_CONCURRENT_MODELS. */
30
+ static final int MAX_CONCURRENT_MODELS = 5;
31
+
32
+ /** Keep this much device RAM free after a model load (headroom for the app + OS). */
33
+ static final long DEFAULT_RESERVE_BYTES = 512L * 1024 * 1024; // 512 MB
34
+
35
+ /** Fallback footprint when the GGUF size cannot be determined. */
36
+ private static final long UNKNOWN_MODEL_FOOTPRINT_BYTES = 512L * 1024 * 1024;
37
+
38
+ enum DeniedBy { LIMIT, MEMORY }
39
+
40
+ /** Result of an admission decision. */
41
+ static final class Decision {
42
+ final boolean allow;
43
+ final DeniedBy deniedBy; // null when allowed
44
+ final String reason; // null when allowed
45
+ final long estimatedBytes;
46
+
47
+ private Decision(boolean allow, DeniedBy deniedBy, String reason, long estimatedBytes) {
48
+ this.allow = allow;
49
+ this.deniedBy = deniedBy;
50
+ this.reason = reason;
51
+ this.estimatedBytes = estimatedBytes;
52
+ }
53
+
54
+ static Decision allowed(long estimatedBytes) {
55
+ return new Decision(true, null, null, estimatedBytes);
56
+ }
57
+
58
+ static Decision denied(DeniedBy deniedBy, String reason, long estimatedBytes) {
59
+ return new Decision(false, deniedBy, reason, estimatedBytes);
60
+ }
61
+ }
62
+
63
+ /** Lightweight snapshot of device memory (mirrors the Web MemorySnapshot). */
64
+ static final class MemorySnapshot {
65
+ final long totalBytes;
66
+ final long freeBytes;
67
+ final boolean lowMemory;
68
+
69
+ MemorySnapshot(long totalBytes, long freeBytes, boolean lowMemory) {
70
+ this.totalBytes = totalBytes;
71
+ this.freeBytes = freeBytes;
72
+ this.lowMemory = lowMemory;
73
+ }
74
+ }
75
+
76
+ private final Context appContext;
77
+ private final int maxModels;
78
+ private final long reserveBytes;
79
+
80
+ ModelAdmissionController(Context appContext) {
81
+ this(appContext, MAX_CONCURRENT_MODELS, DEFAULT_RESERVE_BYTES);
82
+ }
83
+
84
+ ModelAdmissionController(Context appContext, int maxModels, long reserveBytes) {
85
+ this.appContext = appContext;
86
+ this.maxModels = maxModels;
87
+ this.reserveBytes = reserveBytes;
88
+ }
89
+
90
+ /**
91
+ * Estimate the resident footprint of a model once loaded.
92
+ *
93
+ * <p>Native loading (mmap or full read) does not duplicate the GGUF the way a naive
94
+ * heap copy would, but the KV cache and compute buffers add headroom on top of the
95
+ * mapped weights. The multipliers below match the WASM policy's intent, scaled for
96
+ * native memory behaviour.
97
+ *
98
+ * @param fileBytes GGUF file size in bytes (0 or negative when unknown)
99
+ * @param embedding whether the context is an embedding-only model (lighter headroom)
100
+ */
101
+ static long estimateModelFootprint(long fileBytes, boolean embedding) {
102
+ if (fileBytes <= 0) {
103
+ return UNKNOWN_MODEL_FOOTPRINT_BYTES;
104
+ }
105
+ // Weights (mapped) + a proportional slice for KV cache / compute buffers.
106
+ double weightMultiplier;
107
+ if (embedding) {
108
+ weightMultiplier = 1.15;
109
+ } else if (fileBytes > 200L * 1024 * 1024) {
110
+ weightMultiplier = 1.30;
111
+ } else {
112
+ weightMultiplier = 1.20;
113
+ }
114
+ long proportionalHeadroom = (long) Math.ceil(fileBytes * 0.12);
115
+ long minHeadroom = embedding ? 48L * 1024 * 1024 : 96L * 1024 * 1024;
116
+ long headroom = Math.max(minHeadroom, proportionalHeadroom);
117
+ return (long) Math.ceil(fileBytes * weightMultiplier) + headroom;
118
+ }
119
+
120
+ /** Read the GGUF file size, returning 0 when the path is missing or unreadable. */
121
+ static long fileSizeBytes(String modelPath) {
122
+ if (modelPath == null || modelPath.isEmpty()) {
123
+ return 0L;
124
+ }
125
+ try {
126
+ File f = new File(modelPath);
127
+ return f.exists() ? f.length() : 0L;
128
+ } catch (Exception e) {
129
+ return 0L;
130
+ }
131
+ }
132
+
133
+ /** Capture a device memory snapshot from {@link ActivityManager}. */
134
+ MemorySnapshot memorySnapshot() {
135
+ try {
136
+ ActivityManager am = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
137
+ if (am == null) {
138
+ return new MemorySnapshot(0L, 0L, false);
139
+ }
140
+ ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
141
+ am.getMemoryInfo(info);
142
+ return new MemorySnapshot(info.totalMem, info.availMem, info.lowMemory);
143
+ } catch (Exception e) {
144
+ Log.w(TAG, "Failed to read memory snapshot: " + e.getMessage());
145
+ return new MemorySnapshot(0L, 0L, false);
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Decide whether a new model may be admitted.
151
+ *
152
+ * @param currentlyLoaded number of contexts already loaded
153
+ * @param modelPath path to the GGUF being loaded (used to size the footprint)
154
+ * @param embedding whether this is an embedding-only context
155
+ */
156
+ Decision canAdmit(int currentlyLoaded, String modelPath, boolean embedding) {
157
+ long fileBytes = fileSizeBytes(modelPath);
158
+ long estimated = estimateModelFootprint(fileBytes, embedding);
159
+
160
+ // 1. Slot limit (parity with Web).
161
+ if (currentlyLoaded >= maxModels) {
162
+ return Decision.denied(
163
+ DeniedBy.LIMIT,
164
+ "Model slot limit reached (" + maxModels + " concurrent contexts)",
165
+ estimated
166
+ );
167
+ }
168
+
169
+ // 2. Device-memory guard: keep `reserveBytes` free after the load.
170
+ MemorySnapshot mem = memorySnapshot();
171
+ if (mem.lowMemory) {
172
+ return Decision.denied(
173
+ DeniedBy.MEMORY,
174
+ "Device reported low-memory state; refusing to load model",
175
+ estimated
176
+ );
177
+ }
178
+ if (mem.freeBytes > 0) {
179
+ long projectedFree = mem.freeBytes - estimated;
180
+ if (projectedFree < reserveBytes) {
181
+ return Decision.denied(
182
+ DeniedBy.MEMORY,
183
+ "Insufficient memory: need ~" + toMb(estimated) + " MB, "
184
+ + toMb(mem.freeBytes) + " MB free, "
185
+ + toMb(reserveBytes) + " MB reserve required",
186
+ estimated
187
+ );
188
+ }
189
+ }
190
+
191
+ return Decision.allowed(estimated);
192
+ }
193
+
194
+ /** Snapshot of admission state for diagnostics / health reporting. */
195
+ Map<String, Object> status(int currentlyLoaded) {
196
+ MemorySnapshot mem = memorySnapshot();
197
+ Map<String, Object> m = new HashMap<>();
198
+ m.put("loadedModels", currentlyLoaded);
199
+ m.put("maxModels", maxModels);
200
+ m.put("deviceTotalBytes", mem.totalBytes);
201
+ m.put("deviceFreeBytes", mem.freeBytes);
202
+ m.put("reserveBytes", reserveBytes);
203
+ m.put("lowMemory", mem.lowMemory);
204
+ return m;
205
+ }
206
+
207
+ int maxModels() {
208
+ return maxModels;
209
+ }
210
+
211
+ private static long toMb(long bytes) {
212
+ return bytes / (1024 * 1024);
213
+ }
214
+ }
@@ -0,0 +1,261 @@
1
+ // Advanced Chat and Session Management Methods for Android JNI
2
+
3
+ // MARK: - Advanced Chat Methods
4
+
5
+ JNIEXPORT jobject JNICALL
6
+ Java_ai_annadata_plugin_capacitor_LlamaCpp_chatNative(
7
+ JNIEnv* env, jobject thiz, jlong contextId, jobjectArray messages_array,
8
+ jstring system, jstring chat_template, jobject params) {
9
+
10
+ try {
11
+ LOGI("Chat completion for context ID: %ld", contextId);
12
+
13
+ auto it = contexts.find(contextId);
14
+ if (it == contexts.end()) {
15
+ LOGE("Context not found: %ld", contextId);
16
+ throw_java_exception(env, "java/lang/RuntimeException", "Context not found");
17
+ return nullptr;
18
+ }
19
+
20
+ auto& ctx = it->second;
21
+ if (!ctx || !ctx->ctx) {
22
+ LOGE("Invalid context");
23
+ throw_java_exception(env, "java/lang/RuntimeException", "Invalid context");
24
+ return nullptr;
25
+ }
26
+
27
+ // Convert Java messages array to JSON
28
+ jsize messages_length = env->GetArrayLength(messages_array);
29
+ std::string messages_json = "[";
30
+
31
+ for (jsize i = 0; i < messages_length; i++) {
32
+ jobject msg_obj = env->GetObjectArrayElement(messages_array, i);
33
+ jclass hashMapClass = env->GetObjectClass(msg_obj);
34
+ jmethodID getMethod = env->GetMethodID(hashMapClass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
35
+
36
+ // Get role
37
+ jstring roleKey = jni_utils::string_to_jstring(env, "role");
38
+ jstring roleValue = (jstring)env->CallObjectMethod(msg_obj, getMethod, roleKey);
39
+ std::string role = jni_utils::jstring_to_string(env, roleValue);
40
+ env->DeleteLocalRef(roleKey);
41
+ env->DeleteLocalRef(roleValue);
42
+
43
+ // Get content
44
+ jstring contentKey = jni_utils::string_to_jstring(env, "content");
45
+ jstring contentValue = (jstring)env->CallObjectMethod(msg_obj, getMethod, contentKey);
46
+ std::string content = jni_utils::jstring_to_string(env, contentValue);
47
+ env->DeleteLocalRef(contentKey);
48
+ env->DeleteLocalRef(contentValue);
49
+
50
+ if (i > 0) messages_json += ",";
51
+ messages_json += "{\"role\":\"" + role + "\",\"content\":\"" + content + "\"}";
52
+
53
+ env->DeleteLocalRef(msg_obj);
54
+ }
55
+
56
+ messages_json += "]";
57
+
58
+ std::string system_str = jni_utils::jstring_to_string(env, system);
59
+ std::string template_str = jni_utils::jstring_to_string(env, chat_template);
60
+
61
+ LOGI("Chat messages: %s", messages_json.c_str());
62
+ LOGI("System prompt: %s", system_str.c_str());
63
+
64
+ // Format chat prompt
65
+ std::string formatted_prompt = ctx->getFormattedChat(messages_json, template_str);
66
+
67
+ // Extract completion parameters
68
+ jint n_predict = 512;
69
+ jdouble temperature = 0.7;
70
+
71
+ if (params != nullptr) {
72
+ jclass jsObjectClass = env->GetObjectClass(params);
73
+ jmethodID getIntegerMethod = env->GetMethodID(jsObjectClass, "getInteger", "(Ljava/lang/String;)Ljava/lang/Integer;");
74
+ if (env->ExceptionCheck()) {
75
+ env->ExceptionClear();
76
+ getIntegerMethod = nullptr;
77
+ }
78
+
79
+ if (getIntegerMethod != nullptr) {
80
+ jstring key = jni_utils::string_to_jstring(env, "n_predict");
81
+ jobject val = env->CallObjectMethod(params, getIntegerMethod, key);
82
+ if (val != nullptr && !env->ExceptionCheck()) {
83
+ n_predict = env->CallIntMethod(val, env->GetMethodID(env->FindClass("java/lang/Integer"), "intValue", "()I"));
84
+ env->DeleteLocalRef(val);
85
+ } else if (env->ExceptionCheck()) {
86
+ env->ExceptionClear();
87
+ }
88
+ env->DeleteLocalRef(key);
89
+ }
90
+
91
+ temperature = jni_utils::jsobject_opt_double(env, params, "temperature", temperature);
92
+ }
93
+
94
+ // Run completion with formatted prompt
95
+ ctx->params.n_predict = n_predict;
96
+ ctx->params.sampling.temp = temperature;
97
+
98
+ // This would delegate to the completion method
99
+ // For now, return formatted prompt
100
+ jclass hashMapClass = env->FindClass("java/util/HashMap");
101
+ jmethodID hashMapConstructor = env->GetMethodID(hashMapClass, "<init>", "()V");
102
+ jmethodID putMethod = env->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
103
+
104
+ jobject resultMap = env->NewObject(hashMapClass, hashMapConstructor);
105
+
106
+ env->CallObjectMethod(resultMap, putMethod,
107
+ jni_utils::string_to_jstring(env, "formatted_prompt"),
108
+ jni_utils::string_to_jstring(env, formatted_prompt));
109
+
110
+ return resultMap;
111
+
112
+ } catch (const std::exception& e) {
113
+ LOGE("Exception in chat: %s", e.what());
114
+ throw_java_exception(env, "java/lang/RuntimeException", e.what());
115
+ return nullptr;
116
+ }
117
+ }
118
+
119
+ JNIEXPORT jobject JNICALL
120
+ Java_ai_annadata_plugin_capacitor_LlamaCpp_chatWithSystemNative(
121
+ JNIEnv* env, jobject thiz, jlong contextId, jstring system, jstring message, jobject params) {
122
+
123
+ try {
124
+ LOGI("Chat with system for context ID: %ld", contextId);
125
+
126
+ auto it = contexts.find(contextId);
127
+ if (it == contexts.end()) {
128
+ LOGE("Context not found: %ld", contextId);
129
+ throw_java_exception(env, "java/lang/RuntimeException", "Context not found");
130
+ return nullptr;
131
+ }
132
+
133
+ auto& ctx = it->second;
134
+ if (!ctx || !ctx->ctx) {
135
+ LOGE("Invalid context");
136
+ throw_java_exception(env, "java/lang/RuntimeException", "Invalid context");
137
+ return nullptr;
138
+ }
139
+
140
+ std::string system_str = jni_utils::jstring_to_string(env, system);
141
+ std::string message_str = jni_utils::jstring_to_string(env, message);
142
+
143
+ LOGI("System: %s", system_str.c_str());
144
+ LOGI("Message: %s", message_str.c_str());
145
+
146
+ // Build messages array
147
+ std::string messages_json = "[";
148
+ messages_json += "{\"role\":\"system\",\"content\":\"" + system_str + "\"},";
149
+ messages_json += "{\"role\":\"user\",\"content\":\"" + message_str + "\"}";
150
+ messages_json += "]";
151
+
152
+ // Format using default chat template
153
+ std::string formatted_prompt = ctx->getFormattedChat(messages_json, "");
154
+
155
+ LOGI("Formatted prompt length: %zu", formatted_prompt.length());
156
+
157
+ // Return formatted prompt in result map
158
+ jclass hashMapClass = env->FindClass("java/util/HashMap");
159
+ jmethodID hashMapConstructor = env->GetMethodID(hashMapClass, "<init>", "()V");
160
+ jmethodID putMethod = env->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
161
+
162
+ jobject resultMap = env->NewObject(hashMapClass, hashMapConstructor);
163
+
164
+ env->CallObjectMethod(resultMap, putMethod,
165
+ jni_utils::string_to_jstring(env, "formatted_prompt"),
166
+ jni_utils::string_to_jstring(env, formatted_prompt));
167
+
168
+ return resultMap;
169
+
170
+ } catch (const std::exception& e) {
171
+ LOGE("Exception in chatWithSystem: %s", e.what());
172
+ throw_java_exception(env, "java/lang/RuntimeException", e.what());
173
+ return nullptr;
174
+ }
175
+ }
176
+
177
+ JNIEXPORT jobject JNICALL
178
+ Java_ai_annadata_plugin_capacitor_LlamaCpp_generateTextNative(
179
+ JNIEnv* env, jobject thiz, jlong contextId, jstring prompt, jobject params) {
180
+
181
+ try {
182
+ LOGI("Generate text for context ID: %ld", contextId);
183
+
184
+ auto it = contexts.find(contextId);
185
+ if (it == contexts.end()) {
186
+ LOGE("Context not found: %ld", contextId);
187
+ throw_java_exception(env, "java/lang/RuntimeException", "Context not found");
188
+ return nullptr;
189
+ }
190
+
191
+ auto& ctx = it->second;
192
+ if (!ctx || !ctx->ctx) {
193
+ LOGE("Invalid context");
194
+ throw_java_exception(env, "java/lang/RuntimeException", "Invalid context");
195
+ return nullptr;
196
+ }
197
+
198
+ std::string prompt_str = jni_utils::jstring_to_string(env, prompt);
199
+
200
+ // Extract parameters
201
+ jint n_predict = 256;
202
+ jdouble temperature = 0.7;
203
+
204
+ if (params != nullptr) {
205
+ jclass jsObjectClass = env->GetObjectClass(params);
206
+ jmethodID getIntegerMethod = env->GetMethodID(jsObjectClass, "getInteger", "(Ljava/lang/String;)Ljava/lang/Integer;");
207
+ if (env->ExceptionCheck()) {
208
+ env->ExceptionClear();
209
+ getIntegerMethod = nullptr;
210
+ }
211
+
212
+ if (getIntegerMethod != nullptr) {
213
+ jstring key = jni_utils::string_to_jstring(env, "n_predict");
214
+ jobject val = env->CallObjectMethod(params, getIntegerMethod, key);
215
+ if (val != nullptr && !env->ExceptionCheck()) {
216
+ n_predict = env->CallIntMethod(val, env->GetMethodID(env->FindClass("java/lang/Integer"), "intValue", "()I"));
217
+ env->DeleteLocalRef(val);
218
+ } else if (env->ExceptionCheck()) {
219
+ env->ExceptionClear();
220
+ }
221
+ env->DeleteLocalRef(key);
222
+ }
223
+
224
+ temperature = jni_utils::jsobject_opt_double(env, params, "temperature", temperature);
225
+ }
226
+
227
+ LOGI("Generate text prompt: %s, n_predict: %d", prompt_str.c_str(), n_predict);
228
+
229
+ // Set generation parameters
230
+ ctx->params.n_predict = n_predict;
231
+ ctx->params.sampling.temp = temperature;
232
+
233
+ // This would delegate to completion
234
+ jclass hashMapClass = env->FindClass("java/util/HashMap");
235
+ jmethodID hashMapConstructor = env->GetMethodID(hashMapClass, "<init>", "()V");
236
+ jmethodID putMethod = env->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
237
+
238
+ jobject resultMap = env->NewObject(hashMapClass, hashMapConstructor);
239
+
240
+ env->CallObjectMethod(resultMap, putMethod,
241
+ jni_utils::string_to_jstring(env, "prompt"),
242
+ jni_utils::string_to_jstring(env, prompt_str));
243
+
244
+ env->CallObjectMethod(resultMap, putMethod,
245
+ jni_utils::string_to_jstring(env, "n_predict"),
246
+ env->NewObject(env->FindClass("java/lang/Integer"),
247
+ env->GetMethodID(env->FindClass("java/lang/Integer"), "<init>", "(I)V"),
248
+ n_predict));
249
+
250
+ return resultMap;
251
+
252
+ } catch (const std::exception& e) {
253
+ LOGE("Exception in generateText: %s", e.what());
254
+ throw_java_exception(env, "java/lang/RuntimeException", e.what());
255
+ return nullptr;
256
+ }
257
+ }
258
+
259
+ // MARK: - Session Management Methods
260
+ // NOTE: loadSessionNative and saveSessionNative are implemented in jni.cpp
261
+ // with full llama_state_save/load_file support. Stubs removed to avoid redefinition.
@@ -0,0 +1,197 @@
1
+ // LoRA Adapter Methods for Android JNI
2
+ // Note: These are defined as _impl variants; the public JNI entry points in
3
+ // jni.cpp forward to them. This avoids duplicate-symbol conflicts at link time.
4
+
5
+ JNIEXPORT jint JNICALL
6
+ Java_ai_annadata_plugin_capacitor_LlamaCpp_applyLoraAdaptersNative_impl(
7
+ JNIEnv* env, jobject thiz, jlong contextId, jobjectArray loraAdaptersArray) {
8
+
9
+ try {
10
+ LOGI("Applying LoRA adapters for context ID: %ld", contextId);
11
+
12
+ // Find the context
13
+ auto it = contexts.find(contextId);
14
+ if (it == contexts.end()) {
15
+ LOGE("Context not found: %ld", contextId);
16
+ throw_java_exception(env, "java/lang/RuntimeException", "Context not found");
17
+ return -1;
18
+ }
19
+
20
+ auto& ctx = it->second;
21
+ if (!ctx || !ctx->ctx || !ctx->model) {
22
+ LOGE("Invalid context, llama context, or model is null");
23
+ throw_java_exception(env, "java/lang/RuntimeException", "Invalid context or model not loaded");
24
+ return -1;
25
+ }
26
+
27
+ // Convert Java array of HashMap to C++ vector of LoRA info
28
+ std::vector<common_adapter_lora_info> lora_adapters;
29
+
30
+ if (loraAdaptersArray != nullptr) {
31
+ jsize array_length = env->GetArrayLength(loraAdaptersArray);
32
+ LOGI("Processing %d LoRA adapters", array_length);
33
+
34
+ for (jsize i = 0; i < array_length; i++) {
35
+ jobject adapter_obj = env->GetObjectArrayElement(loraAdaptersArray, i);
36
+ if (adapter_obj == nullptr) {
37
+ LOGW("LoRA adapter at index %d is null", i);
38
+ continue;
39
+ }
40
+
41
+ // Get HashMap class
42
+ jclass hashMapClass = env->GetObjectClass(adapter_obj);
43
+ jmethodID getMethod = env->GetMethodID(hashMapClass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
44
+
45
+ // Get path from HashMap
46
+ jstring pathKey = jni_utils::string_to_jstring(env, "path");
47
+ jstring pathValue = (jstring)env->CallObjectMethod(adapter_obj, getMethod, pathKey);
48
+ env->DeleteLocalRef(pathKey);
49
+
50
+ if (pathValue == nullptr) {
51
+ LOGE("LoRA adapter at index %d has no path", i);
52
+ env->DeleteLocalRef(adapter_obj);
53
+ continue;
54
+ }
55
+
56
+ std::string path = jni_utils::jstring_to_string(env, pathValue);
57
+ env->DeleteLocalRef(pathValue);
58
+
59
+ // Get scale from HashMap if provided
60
+ float scale = 1.0f;
61
+ jstring scaleKey = jni_utils::string_to_jstring(env, "scale");
62
+ jobject scaleValue = env->CallObjectMethod(adapter_obj, getMethod, scaleKey);
63
+ env->DeleteLocalRef(scaleKey);
64
+
65
+ if (scaleValue != nullptr) {
66
+ jclass doubleClass = env->FindClass("java/lang/Double");
67
+ jmethodID doubleValueMethod = env->GetMethodID(doubleClass, "doubleValue", "()D");
68
+ scale = static_cast<float>(env->CallDoubleMethod(scaleValue, doubleValueMethod));
69
+ env->DeleteLocalRef(scaleValue);
70
+ LOGI("LoRA adapter %d scale: %.2f", i, scale);
71
+ }
72
+
73
+ // Create LoRA info struct
74
+ common_adapter_lora_info lora_info;
75
+ lora_info.path = path;
76
+ lora_info.scale = scale;
77
+ lora_adapters.push_back(lora_info);
78
+
79
+ LOGI("LoRA adapter %d added: path=%s, scale=%.2f", i, path.c_str(), scale);
80
+ env->DeleteLocalRef(adapter_obj);
81
+ }
82
+ }
83
+
84
+ // Apply LoRA adapters
85
+ LOGI("Calling applyLoraAdapters with %zu adapters", lora_adapters.size());
86
+ int result = ctx->applyLoraAdapters(lora_adapters);
87
+
88
+ if (result != 0) {
89
+ LOGE("Failed to apply LoRA adapters: %d", result);
90
+ throw_java_exception(env, "java/lang/RuntimeException", "Failed to apply LoRA adapters");
91
+ return -1;
92
+ }
93
+
94
+ LOGI("LoRA adapters applied successfully");
95
+ return static_cast<jint>(lora_adapters.size());
96
+
97
+ } catch (const std::exception& e) {
98
+ LOGE("Exception in applyLoraAdapters: %s", e.what());
99
+ throw_java_exception(env, "java/lang/RuntimeException", e.what());
100
+ return -1;
101
+ }
102
+ }
103
+
104
+ JNIEXPORT void JNICALL
105
+ Java_ai_annadata_plugin_capacitor_LlamaCpp_removeLoraAdaptersNative_impl(
106
+ JNIEnv* env, jobject thiz, jlong contextId) {
107
+
108
+ try {
109
+ LOGI("Removing LoRA adapters for context ID: %ld", contextId);
110
+
111
+ auto it = contexts.find(contextId);
112
+ if (it == contexts.end()) {
113
+ LOGE("Context not found: %ld", contextId);
114
+ throw_java_exception(env, "java/lang/RuntimeException", "Context not found");
115
+ return;
116
+ }
117
+
118
+ auto& ctx = it->second;
119
+ if (!ctx || !ctx->ctx) {
120
+ LOGE("Invalid context or llama context is null");
121
+ throw_java_exception(env, "java/lang/RuntimeException", "Invalid context");
122
+ return;
123
+ }
124
+
125
+ ctx->removeLoraAdapters();
126
+ LOGI("LoRA adapters removed successfully");
127
+
128
+ } catch (const std::exception& e) {
129
+ LOGE("Exception in removeLoraAdapters: %s", e.what());
130
+ throw_java_exception(env, "java/lang/RuntimeException", e.what());
131
+ }
132
+ }
133
+
134
+ JNIEXPORT jobject JNICALL
135
+ Java_ai_annadata_plugin_capacitor_LlamaCpp_getLoadedLoraAdaptersNative_impl(
136
+ JNIEnv* env, jobject thiz, jlong contextId) {
137
+
138
+ try {
139
+ LOGI("Getting loaded LoRA adapters for context ID: %ld", contextId);
140
+
141
+ auto it = contexts.find(contextId);
142
+ if (it == contexts.end()) {
143
+ LOGE("Context not found: %ld", contextId);
144
+ throw_java_exception(env, "java/lang/RuntimeException", "Context not found");
145
+ return nullptr;
146
+ }
147
+
148
+ auto& ctx = it->second;
149
+ if (!ctx || !ctx->ctx) {
150
+ LOGE("Invalid context or llama context is null");
151
+ throw_java_exception(env, "java/lang/RuntimeException", "Invalid context");
152
+ return nullptr;
153
+ }
154
+
155
+ // Get loaded adapters
156
+ std::vector<common_adapter_lora_info> adapters = ctx->getLoadedLoraAdapters();
157
+
158
+ // Create Java ArrayList for result
159
+ jclass arrayListClass = env->FindClass("java/util/ArrayList");
160
+ jmethodID arrayListConstructor = env->GetMethodID(arrayListClass, "<init>", "()V");
161
+ jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
162
+
163
+ jobject resultList = env->NewObject(arrayListClass, arrayListConstructor);
164
+
165
+ // Add each adapter to ArrayList
166
+ jclass hashMapClass = env->FindClass("java/util/HashMap");
167
+ jmethodID hashMapConstructor = env->GetMethodID(hashMapClass, "<init>", "()V");
168
+ jmethodID putMethod = env->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
169
+
170
+ for (const auto& adapter : adapters) {
171
+ jobject adapterMap = env->NewObject(hashMapClass, hashMapConstructor);
172
+
173
+ env->CallObjectMethod(adapterMap, putMethod,
174
+ jni_utils::string_to_jstring(env, "path"),
175
+ jni_utils::string_to_jstring(env, adapter.path));
176
+
177
+ env->CallObjectMethod(adapterMap, putMethod,
178
+ jni_utils::string_to_jstring(env, "scale"),
179
+ env->NewObject(env->FindClass("java/lang/Double"),
180
+ env->GetMethodID(env->FindClass("java/lang/Double"), "<init>", "(D)V"),
181
+ static_cast<jdouble>(adapter.scale)));
182
+
183
+ env->CallBooleanMethod(resultList, addMethod, adapterMap);
184
+ env->DeleteLocalRef(adapterMap);
185
+
186
+ LOGI("LoRA adapter: path=%s, scale=%.2f", adapter.path.c_str(), adapter.scale);
187
+ }
188
+
189
+ LOGI("Retrieved %zu loaded LoRA adapters", adapters.size());
190
+ return resultList;
191
+
192
+ } catch (const std::exception& e) {
193
+ LOGE("Exception in getLoadedLoraAdapters: %s", e.what());
194
+ throw_java_exception(env, "java/lang/RuntimeException", e.what());
195
+ return nullptr;
196
+ }
197
+ }