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,1340 @@
1
+ package ai.annadata.plugin.capacitor;
2
+
3
+ import android.util.Log;
4
+ import com.getcapacitor.JSObject;
5
+ import java.util.HashMap;
6
+ import java.util.Map;
7
+ import java.util.Iterator;
8
+ import java.util.concurrent.CompletableFuture;
9
+ import java.io.File;
10
+ import java.io.FileOutputStream;
11
+ import java.io.IOException;
12
+ import java.io.InputStream;
13
+ import java.net.HttpURLConnection;
14
+ import java.net.URL;
15
+ import java.util.List;
16
+ import android.content.Context;
17
+ import android.os.Environment;
18
+ import java.util.ArrayList;
19
+
20
+ // MARK: - Result Types
21
+ class LlamaResult<T> {
22
+ private final T data;
23
+ private final LlamaError error;
24
+ private final boolean isSuccess;
25
+
26
+ private LlamaResult(T data, LlamaError error, boolean isSuccess) {
27
+ this.data = data;
28
+ this.error = error;
29
+ this.isSuccess = isSuccess;
30
+ }
31
+
32
+ public static <T> LlamaResult<T> success(T data) {
33
+ return new LlamaResult<>(data, null, true);
34
+ }
35
+
36
+ public static <T> LlamaResult<T> failure(LlamaError error) {
37
+ return new LlamaResult<>(null, error, false);
38
+ }
39
+
40
+ public boolean isSuccess() {
41
+ return isSuccess;
42
+ }
43
+
44
+ public T getData() {
45
+ return data;
46
+ }
47
+
48
+ public LlamaError getError() {
49
+ return error;
50
+ }
51
+ }
52
+
53
+ class LlamaError extends Exception {
54
+ public LlamaError(String message) {
55
+ super(message);
56
+ }
57
+ }
58
+
59
+ // MARK: - Context Management
60
+ class LlamaContext {
61
+ private final int id;
62
+ private LlamaModel model;
63
+ private boolean isMultimodalEnabled = false;
64
+ private boolean isVocoderEnabled = false;
65
+ private long nativeContextId = -1;
66
+
67
+ public LlamaContext(int id) {
68
+ this.id = id;
69
+ }
70
+
71
+ public int getId() {
72
+ return id;
73
+ }
74
+
75
+ public LlamaModel getModel() {
76
+ return model;
77
+ }
78
+
79
+ public void setModel(LlamaModel model) {
80
+ this.model = model;
81
+ }
82
+
83
+ public boolean isMultimodalEnabled() {
84
+ return isMultimodalEnabled;
85
+ }
86
+
87
+ public void setMultimodalEnabled(boolean multimodalEnabled) {
88
+ isMultimodalEnabled = multimodalEnabled;
89
+ }
90
+
91
+ public boolean isVocoderEnabled() {
92
+ return isVocoderEnabled;
93
+ }
94
+
95
+ public void setVocoderEnabled(boolean vocoderEnabled) {
96
+ isVocoderEnabled = vocoderEnabled;
97
+ }
98
+
99
+ public long getNativeContextId() {
100
+ return nativeContextId;
101
+ }
102
+
103
+ public void setNativeContextId(long nativeContextId) {
104
+ this.nativeContextId = nativeContextId;
105
+ }
106
+ }
107
+
108
+ class LlamaModel {
109
+ private final String path;
110
+ private final String desc;
111
+ private final int size;
112
+ private final int nEmbd;
113
+ private final int nParams;
114
+ private final ChatTemplates chatTemplates;
115
+ private final Map<String, Object> metadata;
116
+
117
+ public LlamaModel(String path, String desc, int size, int nEmbd, int nParams, ChatTemplates chatTemplates, Map<String, Object> metadata) {
118
+ this.path = path;
119
+ this.desc = desc;
120
+ this.size = size;
121
+ this.nEmbd = nEmbd;
122
+ this.nParams = nParams;
123
+ this.chatTemplates = chatTemplates;
124
+ this.metadata = metadata;
125
+ }
126
+
127
+ public String getPath() {
128
+ return path;
129
+ }
130
+
131
+ public String getDesc() {
132
+ return desc;
133
+ }
134
+
135
+ public int getSize() {
136
+ return size;
137
+ }
138
+
139
+ public int getNEmbd() {
140
+ return nEmbd;
141
+ }
142
+
143
+ public int getNParams() {
144
+ return nParams;
145
+ }
146
+
147
+ public ChatTemplates getChatTemplates() {
148
+ return chatTemplates;
149
+ }
150
+
151
+ public Map<String, Object> getMetadata() {
152
+ return metadata;
153
+ }
154
+ }
155
+
156
+ class ChatTemplates {
157
+ private final boolean llamaChat;
158
+ private final MinjaTemplates minja;
159
+
160
+ public ChatTemplates(boolean llamaChat, MinjaTemplates minja) {
161
+ this.llamaChat = llamaChat;
162
+ this.minja = minja;
163
+ }
164
+
165
+ public boolean isLlamaChat() {
166
+ return llamaChat;
167
+ }
168
+
169
+ public MinjaTemplates getMinja() {
170
+ return minja;
171
+ }
172
+ }
173
+
174
+ class MinjaTemplates {
175
+ private final boolean default_;
176
+ private final MinjaCaps defaultCaps;
177
+ private final boolean toolUse;
178
+ private final MinjaCaps toolUseCaps;
179
+
180
+ public MinjaTemplates(boolean default_, MinjaCaps defaultCaps, boolean toolUse, MinjaCaps toolUseCaps) {
181
+ this.default_ = default_;
182
+ this.defaultCaps = defaultCaps;
183
+ this.toolUse = toolUse;
184
+ this.toolUseCaps = toolUseCaps;
185
+ }
186
+
187
+ public boolean isDefault() {
188
+ return default_;
189
+ }
190
+
191
+ public MinjaCaps getDefaultCaps() {
192
+ return defaultCaps;
193
+ }
194
+
195
+ public boolean isToolUse() {
196
+ return toolUse;
197
+ }
198
+
199
+ public MinjaCaps getToolUseCaps() {
200
+ return toolUseCaps;
201
+ }
202
+ }
203
+
204
+ class MinjaCaps {
205
+ private final boolean tools;
206
+ private final boolean toolCalls;
207
+ private final boolean toolResponses;
208
+ private final boolean systemRole;
209
+ private final boolean parallelToolCalls;
210
+ private final boolean toolCallId;
211
+
212
+ public MinjaCaps(boolean tools, boolean toolCalls, boolean toolResponses, boolean systemRole, boolean parallelToolCalls, boolean toolCallId) {
213
+ this.tools = tools;
214
+ this.toolCalls = toolCalls;
215
+ this.toolResponses = toolResponses;
216
+ this.systemRole = systemRole;
217
+ this.parallelToolCalls = parallelToolCalls;
218
+ this.toolCallId = toolCallId;
219
+ }
220
+
221
+ public boolean isTools() {
222
+ return tools;
223
+ }
224
+
225
+ public boolean isToolCalls() {
226
+ return toolCalls;
227
+ }
228
+
229
+ public boolean isToolResponses() {
230
+ return toolResponses;
231
+ }
232
+
233
+ public boolean isSystemRole() {
234
+ return systemRole;
235
+ }
236
+
237
+ public boolean isParallelToolCalls() {
238
+ return parallelToolCalls;
239
+ }
240
+
241
+ public boolean isToolCallId() {
242
+ return toolCallId;
243
+ }
244
+ }
245
+
246
+ // MARK: - Main Implementation
247
+ public class LlamaCpp {
248
+ private static final String TAG = "LlamaCpp";
249
+ private final Map<Integer, LlamaContext> contexts = new HashMap<>();
250
+ private int contextCounter = 0;
251
+ // Default aligned with the isomorphic limit (parity with WASM_MAX_CONCURRENT_MODELS = 5).
252
+ private int contextLimit = ModelAdmissionController.MAX_CONCURRENT_MODELS;
253
+ private boolean nativeLogEnabled = false;
254
+ private Context context;
255
+ // Memory admission control (parity with the Web DefaultModelScheduler).
256
+ private final ModelAdmissionController admissionController;
257
+
258
+ // Constructor to receive context
259
+ public LlamaCpp(Context context) {
260
+ this.context = context;
261
+ this.admissionController = new ModelAdmissionController(context);
262
+ }
263
+
264
+ // Native method declarations
265
+ private native long initContextNative(String modelPath, String[] searchPaths, JSObject params);
266
+ private native void releaseContextNative(long nativeContextId);
267
+ private native Map<String, Object> completionNative(long contextId, JSObject params);
268
+ private native Map<String, Object> modelInfoNative(String modelPath);
269
+ private native void stopCompletionNative(long contextId);
270
+ private native String getFormattedChatNative(long contextId, String messages, String chatTemplate);
271
+ private native boolean toggleNativeLogNative(boolean enabled);
272
+
273
+ // Tokenization methods
274
+ private native Map<String, Object> tokenizeNative(long contextId, String text, String[] imagePaths);
275
+ private native String detokenizeNative(long contextId, int[] tokens);
276
+
277
+ // Embedding and reranking methods
278
+ private native Map<String, Object> embeddingNative(long contextId, String text, JSObject params);
279
+ private native List<Map<String, Object>> rerankNative(long contextId, String query, String[] documents, JSObject params);
280
+
281
+ // Benchmarking
282
+ private native String benchNative(long contextId, int pp, int tg, int pl, int nr);
283
+
284
+ // Session management
285
+ private native Map<String, Object> loadSessionNative(long contextId, String filepath);
286
+ private native int saveSessionNative(long contextId, String filepath, int size);
287
+
288
+ // LoRA adapter methods
289
+ private native int applyLoraAdaptersNative(long contextId, Object[] loraAdapters);
290
+ private native void removeLoraAdaptersNative(long contextId);
291
+ private native List<Map<String, Object>> getLoadedLoraAdaptersNative(long contextId);
292
+
293
+ // Multimodal methods
294
+ private native boolean initMultimodalNative(long contextId, String mmProjPath, boolean useGpu);
295
+ private native boolean isMultimodalEnabledNative(long contextId);
296
+ private native Map<String, Object> getMultimodalSupportNative(long contextId);
297
+ private native void releaseMultimodalNative(long contextId);
298
+
299
+ // TTS / Vocoder methods
300
+ private native boolean initVocoderNative(long contextId, String vocoderModelPath, int nBatch);
301
+ private native boolean isVocoderEnabledNative(long contextId);
302
+ private native Map<String, Object> getFormattedAudioCompletionNative(long contextId, String speakerJsonStr, String textToSpeak);
303
+ private native List<Integer> getAudioCompletionGuideTokensNative(long contextId, String textToSpeak);
304
+ private native List<Float> decodeAudioTokensNative(long contextId, int[] tokens);
305
+ private native void releaseVocoderNative(long contextId);
306
+
307
+ // Model download and management methods
308
+ private native String downloadModelNative(String url, String filename);
309
+ private native Map<String, Object> getDownloadProgressNative(String url);
310
+ private native boolean cancelDownloadNative(String url);
311
+ private native List<Map<String, Object>> getAvailableModelsNative();
312
+
313
+ // Grammar utilities
314
+ private native String convertJsonSchemaToGrammarNative(String schemaJson);
315
+
316
+ /** In-process localhost HTTP server (native); see cap-native-server.cpp */
317
+ private native boolean startLlamaServerNative(String modelPath, String host, int port, String paramsJson);
318
+ private native void stopLlamaServerNative();
319
+ private native boolean isLlamaServerRunningNative();
320
+
321
+ static {
322
+ try {
323
+
324
+ // Detect the current architecture and load the appropriate library
325
+ String arch = System.getProperty("os.arch");
326
+ String abi = android.os.Build.SUPPORTED_ABIS[0]; // Get primary ABI
327
+ String libraryName;
328
+
329
+ // Map Android ABI to library name
330
+ switch (abi) {
331
+ case "arm64-v8a":
332
+ libraryName = "llama-cpp-arm64";
333
+ break;
334
+ case "armeabi-v7a":
335
+ libraryName = "llama-cpp-armeabi";
336
+ break;
337
+ case "x86":
338
+ libraryName = "llama-cpp-x86";
339
+ break;
340
+ case "x86_64":
341
+ libraryName = "llama-cpp-x86_64";
342
+ break;
343
+ default:
344
+ Log.w(TAG, "Unsupported ABI: " + abi + ", falling back to arm64-v8a");
345
+ libraryName = "llama-cpp-arm64";
346
+ break;
347
+ }
348
+
349
+ Log.i(TAG, "Loading native library for ABI: " + abi + " (library: " + libraryName + ")");
350
+ System.loadLibrary(libraryName);
351
+ Log.i(TAG, "Successfully loaded llama-cpp native library: " + libraryName);
352
+ } catch (UnsatisfiedLinkError e) {
353
+ Log.e(TAG, "Failed to load llama-cpp native library: " + e.getMessage());
354
+ throw e;
355
+ }
356
+ }
357
+
358
+ // MARK: - Core initialization and management
359
+
360
+ public void toggleNativeLog(boolean enabled, LlamaCallback<Void> callback) {
361
+ try {
362
+ boolean result = toggleNativeLogNative(enabled);
363
+ nativeLogEnabled = enabled;
364
+ if (enabled) {
365
+ Log.i(TAG, "Native logging enabled");
366
+ } else {
367
+ Log.i(TAG, "Native logging disabled");
368
+ }
369
+ callback.onResult(LlamaResult.success(null));
370
+ } catch (Exception e) {
371
+ callback.onResult(LlamaResult.failure(new LlamaError("Failed to toggle native log: " + e.getMessage())));
372
+ }
373
+ }
374
+
375
+ public void setContextLimit(int limit, LlamaCallback<Void> callback) {
376
+ int maxAllowed = admissionController.maxModels();
377
+ if (limit < 1) {
378
+ callback.onResult(LlamaResult.failure(new LlamaError("Context limit must be at least 1")));
379
+ return;
380
+ }
381
+ if (limit > maxAllowed) {
382
+ // Clamp to the isomorphic hard cap rather than silently over-committing memory.
383
+ Log.w(TAG, "Requested context limit " + limit + " exceeds max " + maxAllowed
384
+ + "; clamping to " + maxAllowed);
385
+ contextLimit = maxAllowed;
386
+ } else {
387
+ contextLimit = limit;
388
+ }
389
+ Log.i(TAG, "Context limit set to " + contextLimit);
390
+ callback.onResult(LlamaResult.success(null));
391
+ }
392
+
393
+ public void downloadModel(String url, String filename, LlamaCallback<String> callback) {
394
+ try {
395
+ Log.i(TAG, "Starting download of model: " + filename + " from: " + url);
396
+ String localPath = downloadModelNative(url, filename);
397
+
398
+ // Start download in background thread
399
+ new Thread(() -> {
400
+ try {
401
+ downloadFile(url, localPath, callback);
402
+ } catch (Exception e) {
403
+ Log.e(TAG, "Error in download thread: " + e.getMessage());
404
+ callback.onResult(LlamaResult.failure(new LlamaError("Download failed: " + e.getMessage())));
405
+ }
406
+ }).start();
407
+
408
+ // Return the local path immediately
409
+ callback.onResult(LlamaResult.success(localPath));
410
+
411
+ } catch (Exception e) {
412
+ Log.e(TAG, "Error preparing download: " + e.getMessage());
413
+ callback.onResult(LlamaResult.failure(new LlamaError("Download preparation failed: " + e.getMessage())));
414
+ }
415
+ }
416
+
417
+ private void downloadFile(String url, String localPath, LlamaCallback<String> callback) {
418
+ try {
419
+ URL downloadUrl = new URL(url);
420
+ HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection();
421
+ connection.setRequestMethod("GET");
422
+ connection.setConnectTimeout(30000);
423
+ connection.setReadTimeout(0); // No timeout for large files
424
+
425
+ int responseCode = connection.getResponseCode();
426
+ if (responseCode != HttpURLConnection.HTTP_OK) {
427
+ throw new IOException("HTTP error code: " + responseCode);
428
+ }
429
+
430
+ long fileSize = connection.getContentLengthLong();
431
+ Log.i(TAG, "File size: " + fileSize + " bytes");
432
+
433
+ try (InputStream inputStream = connection.getInputStream();
434
+ FileOutputStream outputStream = new FileOutputStream(localPath)) {
435
+
436
+ byte[] buffer = new byte[8192];
437
+ long downloadedBytes = 0;
438
+ int bytesRead;
439
+
440
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
441
+ outputStream.write(buffer, 0, bytesRead);
442
+ downloadedBytes += bytesRead;
443
+
444
+ // Log progress every 1MB
445
+ if (downloadedBytes % (1024 * 1024) == 0) {
446
+ double progress = fileSize > 0 ? (double) downloadedBytes / fileSize * 100 : 0;
447
+ Log.i(TAG, String.format("Download progress: %.1f%% (%d/%d bytes)",
448
+ progress, downloadedBytes, fileSize));
449
+ }
450
+ }
451
+ }
452
+
453
+ Log.i(TAG, "Download completed successfully: " + localPath);
454
+ callback.onResult(LlamaResult.success(localPath));
455
+
456
+ } catch (Exception e) {
457
+ Log.e(TAG, "Download failed: " + e.getMessage());
458
+ // Clean up partial file
459
+ try {
460
+ new File(localPath).delete();
461
+ } catch (Exception ignored) {}
462
+
463
+ callback.onResult(LlamaResult.failure(new LlamaError("Download failed: " + e.getMessage())));
464
+ }
465
+ }
466
+
467
+ public void getDownloadProgress(String url, LlamaCallback<Map<String, Object>> callback) {
468
+ try {
469
+ Map<String, Object> progress = getDownloadProgressNative(url);
470
+ if (progress != null) {
471
+ callback.onResult(LlamaResult.success(progress));
472
+ } else {
473
+ callback.onResult(LlamaResult.failure(new LlamaError("No download in progress for this URL")));
474
+ }
475
+ } catch (Exception e) {
476
+ Log.e(TAG, "Error getting download progress: " + e.getMessage());
477
+ callback.onResult(LlamaResult.failure(new LlamaError("Failed to get progress: " + e.getMessage())));
478
+ }
479
+ }
480
+
481
+ public void cancelDownload(String url, LlamaCallback<Boolean> callback) {
482
+ try {
483
+ boolean cancelled = cancelDownloadNative(url);
484
+ callback.onResult(LlamaResult.success(cancelled));
485
+ } catch (Exception e) {
486
+ Log.e(TAG, "Error cancelling download: " + e.getMessage());
487
+ callback.onResult(LlamaResult.failure(new LlamaError("Failed to cancel download: " + e.getMessage())));
488
+ }
489
+ }
490
+
491
+ public void getAvailableModels(LlamaCallback<List<Map<String, Object>>> callback) {
492
+ try {
493
+ List<Map<String, Object>> models = getAvailableModelsNative();
494
+ callback.onResult(LlamaResult.success(models));
495
+ } catch (Exception e) {
496
+ Log.e(TAG, "Error getting available models: " + e.getMessage());
497
+ callback.onResult(LlamaResult.failure(new LlamaError("Failed to get models: " + e.getMessage())));
498
+ }
499
+ }
500
+
501
+ public void startNativeLlamaServer(String modelPath, String host, int port, JSObject params, LlamaCallback<JSObject> callback) {
502
+ try {
503
+ String pj = (params != null) ? params.toString() : "{}";
504
+ String h = (host != null && !host.isEmpty()) ? host : "127.0.0.1";
505
+ boolean ok = startLlamaServerNative(modelPath, h, port, pj);
506
+ JSObject r = new JSObject();
507
+ r.put("running", ok);
508
+ if (ok) {
509
+ callback.onResult(LlamaResult.success(r));
510
+ } else {
511
+ callback.onResult(LlamaResult.failure(new LlamaError("startNativeLlamaServer failed")));
512
+ }
513
+ } catch (Exception e) {
514
+ callback.onResult(LlamaResult.failure(new LlamaError(e.getMessage())));
515
+ }
516
+ }
517
+
518
+ public void stopNativeLlamaServer(LlamaCallback<Void> callback) {
519
+ try {
520
+ stopLlamaServerNative();
521
+ callback.onResult(LlamaResult.success(null));
522
+ } catch (Exception e) {
523
+ callback.onResult(LlamaResult.failure(new LlamaError(e.getMessage())));
524
+ }
525
+ }
526
+
527
+ public void isNativeLlamaServerRunning(LlamaCallback<JSObject> callback) {
528
+ try {
529
+ boolean running = isLlamaServerRunningNative();
530
+ JSObject r = new JSObject();
531
+ r.put("running", running);
532
+ callback.onResult(LlamaResult.success(r));
533
+ } catch (Exception e) {
534
+ callback.onResult(LlamaResult.failure(new LlamaError(e.getMessage())));
535
+ }
536
+ }
537
+
538
+ public void convertJsonSchemaToGrammar(String schemaJson, LlamaCallback<String> callback) {
539
+ try {
540
+ String grammar = convertJsonSchemaToGrammarNative(schemaJson);
541
+ callback.onResult(LlamaResult.success(grammar));
542
+ } catch (Exception e) {
543
+ Log.e(TAG, "Error converting JSON schema to grammar: " + e.getMessage());
544
+ callback.onResult(LlamaResult.failure(new LlamaError("Failed to convert schema: " + e.getMessage())));
545
+ }
546
+ }
547
+
548
+ public void modelInfo(String path, String[] skip, LlamaCallback<Map<String, Object>> callback) {
549
+ try {
550
+ // Call native method to get actual model info
551
+ Map<String, Object> modelInfo = modelInfoNative(path);
552
+ if (modelInfo != null) {
553
+ callback.onResult(LlamaResult.success(modelInfo));
554
+ } else {
555
+ // Fallback to basic info if native method fails
556
+ Map<String, Object> fallbackInfo = new HashMap<>();
557
+ fallbackInfo.put("path", path);
558
+ fallbackInfo.put("desc", "Model file found but info unavailable");
559
+ fallbackInfo.put("size", 0);
560
+ fallbackInfo.put("nEmbd", 0);
561
+ fallbackInfo.put("nParams", 0);
562
+ callback.onResult(LlamaResult.success(fallbackInfo));
563
+ }
564
+ } catch (Exception e) {
565
+ Log.e(TAG, "Error getting model info: " + e.getMessage());
566
+ // Return error info
567
+ Map<String, Object> errorInfo = new HashMap<>();
568
+ errorInfo.put("path", path);
569
+ errorInfo.put("desc", "Error reading model: " + e.getMessage());
570
+ errorInfo.put("size", 0);
571
+ errorInfo.put("nEmbd", 0);
572
+ errorInfo.put("nParams", 0);
573
+ callback.onResult(LlamaResult.success(errorInfo));
574
+ }
575
+ }
576
+
577
+ public void initContext(int contextId, JSObject params, LlamaCallback<Map<String, Object>> callback) {
578
+ try {
579
+ // Extract parameters
580
+ String modelPath = params.getString("model", "");
581
+ if (modelPath == null || modelPath.isEmpty()) {
582
+ callback.onResult(LlamaResult.failure(new LlamaError("Model path is required")));
583
+ return;
584
+ }
585
+
586
+ // Memory admission control: enforce the concurrent-model slot limit AND a
587
+ // device-memory guard before touching native code (parity with the Web path).
588
+ boolean embedding = params.getBoolean("embedding", false);
589
+ ModelAdmissionController.Decision decision =
590
+ admissionController.canAdmit(contexts.size(), modelPath, embedding);
591
+ if (!decision.allow) {
592
+ String prefix = decision.deniedBy == ModelAdmissionController.DeniedBy.LIMIT
593
+ ? "MODEL_LIMIT_REACHED: "
594
+ : "INSUFFICIENT_MEMORY: ";
595
+ Log.w(TAG, "Admission rejected for context " + contextId + " — " + decision.reason);
596
+ callback.onResult(LlamaResult.failure(new LlamaError(prefix + decision.reason)));
597
+ return;
598
+ }
599
+
600
+ String filename = new File(modelPath).getName();
601
+
602
+ // Get dynamic search paths
603
+ String[] searchPaths = getModelSearchPaths(filename);
604
+
605
+ // Call native initialization
606
+ long nativeContextId = initContextNative(modelPath, searchPaths, params);
607
+ if (nativeContextId < 0) {
608
+ callback.onResult(LlamaResult.failure(new LlamaError("Failed to initialize native context")));
609
+ return;
610
+ }
611
+
612
+ // Create Java context wrapper
613
+ LlamaContext context = new LlamaContext(contextId);
614
+ context.setNativeContextId(nativeContextId);
615
+ contexts.put(contextId, context);
616
+
617
+ // Query actual model metadata from native layer
618
+ Map<String, Object> modelInfoMap = modelInfoNative(modelPath);
619
+ String modelDesc = "Loaded model";
620
+ long modelSize = 0;
621
+ int nEmbd = 0;
622
+ int nParams = 0;
623
+ boolean gpuEnabled = false;
624
+ String reasonNoGPU = "GPU not requested";
625
+ if (modelInfoMap != null) {
626
+ Object d = modelInfoMap.get("desc");
627
+ if (d instanceof String) modelDesc = (String) d;
628
+ Object s = modelInfoMap.get("size");
629
+ if (s instanceof Number) modelSize = ((Number) s).longValue();
630
+ Object e = modelInfoMap.get("nEmbd");
631
+ if (e instanceof Number) nEmbd = ((Number) e).intValue();
632
+ Object np = modelInfoMap.get("nParams");
633
+ if (np instanceof Number) nParams = ((Number) np).intValue();
634
+ Object g = modelInfoMap.get("gpu");
635
+ if (g instanceof Boolean) gpuEnabled = (Boolean) g;
636
+ Object rng = modelInfoMap.get("reasonNoGPU");
637
+ if (rng instanceof String) reasonNoGPU = (String) rng;
638
+ }
639
+ // Reflect whether n_gpu_layers > 0 was requested
640
+ int requestedGpuLayers = 0;
641
+ try { requestedGpuLayers = params.getInteger("n_gpu_layers", 0); } catch (Exception ignored) {}
642
+ if (requestedGpuLayers > 0 && !gpuEnabled) {
643
+ reasonNoGPU = "Vulkan/GPU backend not available on this device";
644
+ } else if (requestedGpuLayers == 0) {
645
+ reasonNoGPU = "n_gpu_layers=0 (CPU-only)";
646
+ }
647
+
648
+ Map<String, Object> modelInfo = new HashMap<>();
649
+ modelInfo.put("desc", modelDesc);
650
+ modelInfo.put("size", modelSize);
651
+ modelInfo.put("nEmbd", nEmbd);
652
+ modelInfo.put("nParams", nParams);
653
+ modelInfo.put("path", modelPath);
654
+ Map<String, Object> defaultCaps = new HashMap<>();
655
+ defaultCaps.put("tools", true); defaultCaps.put("toolCalls", true);
656
+ defaultCaps.put("toolResponses", true); defaultCaps.put("systemRole", true);
657
+ defaultCaps.put("parallelToolCalls", true); defaultCaps.put("toolCallId", true);
658
+ Map<String, Object> minja = new HashMap<>();
659
+ minja.put("default", true); minja.put("defaultCaps", defaultCaps);
660
+ minja.put("toolUse", true); minja.put("toolUseCaps", defaultCaps);
661
+ Map<String, Object> chatTemplates = new HashMap<>();
662
+ chatTemplates.put("llamaChat", true);
663
+ chatTemplates.put("minja", minja);
664
+ modelInfo.put("chatTemplates", chatTemplates);
665
+ modelInfo.put("metadata", new HashMap<>());
666
+ modelInfo.put("isChatTemplateSupported", true);
667
+
668
+ Map<String, Object> contextInfo = new HashMap<>();
669
+ contextInfo.put("contextId", contextId);
670
+ contextInfo.put("gpu", gpuEnabled);
671
+ contextInfo.put("reasonNoGPU", reasonNoGPU);
672
+ contextInfo.put("model", modelInfo);
673
+ contextInfo.put("androidLib", "llama-cpp");
674
+
675
+ callback.onResult(LlamaResult.success(contextInfo));
676
+
677
+ } catch (Exception e) {
678
+ callback.onResult(LlamaResult.failure(new LlamaError("Context initialization failed: " + e.getMessage())));
679
+ }
680
+ }
681
+
682
+ public void releaseContext(int contextId, LlamaCallback<Void> callback) {
683
+ LlamaContext context = contexts.get(contextId);
684
+ if (context == null) {
685
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
686
+ return;
687
+ }
688
+
689
+ try {
690
+ // Release native context
691
+ if (context.getNativeContextId() >= 0) {
692
+ releaseContextNative(context.getNativeContextId());
693
+ }
694
+
695
+ // Remove from Java context map
696
+ contexts.remove(contextId);
697
+
698
+ callback.onResult(LlamaResult.success(null));
699
+
700
+ } catch (Exception e) {
701
+ callback.onResult(LlamaResult.failure(new LlamaError("Failed to release context: " + e.getMessage())));
702
+ }
703
+ }
704
+
705
+ public void releaseAllContexts(LlamaCallback<Void> callback) {
706
+ contexts.clear();
707
+ callback.onResult(LlamaResult.success(null));
708
+ }
709
+
710
+ // MARK: - Chat and completion
711
+
712
+ public void getFormattedChat(int contextId, String messages, String chatTemplate, JSObject params, LlamaCallback<Map<String, Object>> callback) {
713
+ LlamaContext context = contexts.get(contextId);
714
+ if (context == null) {
715
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
716
+ return;
717
+ }
718
+
719
+ try {
720
+ // Call native formatted chat
721
+ String result = getFormattedChatNative(context.getNativeContextId(), messages, chatTemplate);
722
+
723
+ // Build formatted chat result - use Lists instead of arrays
724
+ Map<String, Object> formattedChat = new HashMap<>();
725
+ formattedChat.put("type", "llama-chat");
726
+ formattedChat.put("prompt", result);
727
+ formattedChat.put("has_media", false);
728
+ formattedChat.put("media_paths", new ArrayList<String>());
729
+
730
+ callback.onResult(LlamaResult.success(formattedChat));
731
+
732
+ } catch (Exception e) {
733
+ callback.onResult(LlamaResult.failure(new LlamaError("Failed to format chat: " + e.getMessage())));
734
+ }
735
+ }
736
+
737
+ public void completion(int contextId, JSObject params, LlamaCallback<Map<String, Object>> callback) {
738
+ LlamaContext context = contexts.get(contextId);
739
+ if (context == null) {
740
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
741
+ return;
742
+ }
743
+
744
+ try {
745
+ Log.i(TAG, "Starting completion for context: " + contextId);
746
+
747
+ // Call native completion with full params
748
+ Map<String, Object> result = completionNative(context.getNativeContextId(), params);
749
+
750
+ if (result != null) {
751
+ Log.i(TAG, "Completion completed successfully");
752
+ callback.onResult(LlamaResult.success(result));
753
+ } else {
754
+ Log.e(TAG, "Completion returned null result");
755
+ callback.onResult(LlamaResult.failure(new LlamaError("Completion failed")));
756
+ }
757
+
758
+ } catch (Exception e) {
759
+ callback.onResult(LlamaResult.failure(new LlamaError("Completion failed: " + e.getMessage())));
760
+ }
761
+ }
762
+
763
+ public void stopCompletion(int contextId, LlamaCallback<Void> callback) {
764
+ LlamaContext context = contexts.get(contextId);
765
+ if (context == null) {
766
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
767
+ return;
768
+ }
769
+
770
+ try {
771
+ stopCompletionNative(context.getNativeContextId());
772
+ callback.onResult(LlamaResult.success(null));
773
+ } catch (Exception e) {
774
+ callback.onResult(LlamaResult.failure(new LlamaError("Failed to stop completion: " + e.getMessage())));
775
+ }
776
+ }
777
+
778
+ // MARK: - Chat-first methods (like llama-cli -sys)
779
+
780
+ public void chat(int contextId, String messagesJson, String system, String chatTemplate, JSObject params, LlamaCallback<Map<String, Object>> callback) {
781
+ LlamaContext context = contexts.get(contextId);
782
+ if (context == null) {
783
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
784
+ return;
785
+ }
786
+
787
+ try {
788
+ Log.i(TAG, "Starting chat for context: " + contextId);
789
+
790
+ // Parse messages JSON
791
+ List<Map<String, Object>> messages = parseMessagesJson(messagesJson);
792
+
793
+ // Add system message if provided
794
+ if (system != null && !system.isEmpty()) {
795
+ Map<String, Object> systemMsg = new HashMap<>();
796
+ systemMsg.put("role", "system");
797
+ systemMsg.put("content", system);
798
+ messages.add(0, systemMsg); // Add system message at the beginning
799
+ }
800
+
801
+ // Convert messages to JSON string for getFormattedChat
802
+ String formattedMessages = convertMessagesToJson(messages);
803
+
804
+ // First, format the chat
805
+ String formattedPrompt = getFormattedChatNative(context.getNativeContextId(), formattedMessages, chatTemplate != null ? chatTemplate : "");
806
+
807
+ // Then run completion with the formatted prompt
808
+ JSObject completionParams = new JSObject();
809
+ completionParams.put("prompt", formattedPrompt);
810
+
811
+ // Copy other parameters from params
812
+ if (params != null) {
813
+ Iterator<String> keyIterator = params.keys();
814
+ while (keyIterator.hasNext()) {
815
+ String key = keyIterator.next();
816
+ if (!key.equals("prompt") && !key.equals("messages")) {
817
+ completionParams.put(key, params.get(key));
818
+ }
819
+ }
820
+ }
821
+
822
+ // Call native completion
823
+ Map<String, Object> result = completionNative(context.getNativeContextId(), completionParams);
824
+
825
+ if (result != null) {
826
+ Log.i(TAG, "Chat completed successfully");
827
+ callback.onResult(LlamaResult.success(result));
828
+ } else {
829
+ Log.e(TAG, "Chat returned null result");
830
+ callback.onResult(LlamaResult.failure(new LlamaError("Chat failed")));
831
+ }
832
+
833
+ } catch (Exception e) {
834
+ Log.e(TAG, "Chat failed: " + e.getMessage());
835
+ callback.onResult(LlamaResult.failure(new LlamaError("Chat failed: " + e.getMessage())));
836
+ }
837
+ }
838
+
839
+ public void chatWithSystem(int contextId, String system, String message, JSObject params, LlamaCallback<Map<String, Object>> callback) {
840
+ try {
841
+ // Create a simple message array
842
+ List<Map<String, Object>> messages = new ArrayList<>();
843
+ Map<String, Object> userMsg = new HashMap<>();
844
+ userMsg.put("role", "user");
845
+ userMsg.put("content", message);
846
+ messages.add(userMsg);
847
+
848
+ // Call the main chat method
849
+ chat(contextId, convertMessagesToJson(messages), system, null, params, callback);
850
+ } catch (Exception e) {
851
+ callback.onResult(LlamaResult.failure(new LlamaError("Chat with system failed: " + e.getMessage())));
852
+ }
853
+ }
854
+
855
+ public void generateText(int contextId, String prompt, JSObject params, LlamaCallback<Map<String, Object>> callback) {
856
+ LlamaContext context = contexts.get(contextId);
857
+ if (context == null) {
858
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
859
+ return;
860
+ }
861
+
862
+ try {
863
+ Log.i(TAG, "Starting text generation for context: " + contextId);
864
+
865
+ // Create completion parameters
866
+ JSObject completionParams = new JSObject();
867
+ completionParams.put("prompt", prompt);
868
+
869
+ // Copy other parameters from params
870
+ if (params != null) {
871
+ Iterator<String> keyIterator = params.keys();
872
+ while (keyIterator.hasNext()) {
873
+ String key = keyIterator.next();
874
+ if (!key.equals("prompt") && !key.equals("messages")) {
875
+ completionParams.put(key, params.get(key));
876
+ }
877
+ }
878
+ }
879
+
880
+ // Call native completion
881
+ Map<String, Object> result = completionNative(context.getNativeContextId(), completionParams);
882
+
883
+ if (result != null) {
884
+ Log.i(TAG, "Text generation completed successfully");
885
+ callback.onResult(LlamaResult.success(result));
886
+ } else {
887
+ Log.e(TAG, "Text generation returned null result");
888
+ callback.onResult(LlamaResult.failure(new LlamaError("Text generation failed")));
889
+ }
890
+
891
+ } catch (Exception e) {
892
+ Log.e(TAG, "Text generation failed: " + e.getMessage());
893
+ callback.onResult(LlamaResult.failure(new LlamaError("Text generation failed: " + e.getMessage())));
894
+ }
895
+ }
896
+
897
+ // Helper methods for message handling
898
+ private List<Map<String, Object>> parseMessagesJson(String messagesJson) {
899
+ List<Map<String, Object>> messages = new ArrayList<>();
900
+ try {
901
+ // Parse JSON string to extract messages
902
+ org.json.JSONArray jsonArray = new org.json.JSONArray(messagesJson);
903
+ for (int i = 0; i < jsonArray.length(); i++) {
904
+ org.json.JSONObject jsonMessage = jsonArray.getJSONObject(i);
905
+ Map<String, Object> message = new HashMap<>();
906
+ message.put("role", jsonMessage.getString("role"));
907
+ message.put("content", jsonMessage.getString("content"));
908
+ messages.add(message);
909
+ }
910
+ } catch (Exception e) {
911
+ Log.e(TAG, "Error parsing messages JSON: " + e.getMessage());
912
+ // Return empty list on error
913
+ }
914
+ return messages;
915
+ }
916
+
917
+ private String convertMessagesToJson(List<Map<String, Object>> messages) {
918
+ try {
919
+ org.json.JSONArray jsonArray = new org.json.JSONArray();
920
+ for (Map<String, Object> message : messages) {
921
+ org.json.JSONObject jsonMessage = new org.json.JSONObject();
922
+ jsonMessage.put("role", message.get("role"));
923
+ jsonMessage.put("content", message.get("content"));
924
+ jsonArray.put(jsonMessage);
925
+ }
926
+ return jsonArray.toString();
927
+ } catch (Exception e) {
928
+ Log.e(TAG, "Error converting messages to JSON: " + e.getMessage());
929
+ return "[]"; // Return empty array on error
930
+ }
931
+ }
932
+
933
+ // MARK: - Session management
934
+
935
+ public void loadSession(int contextId, String filepath, LlamaCallback<Map<String, Object>> callback) {
936
+ LlamaContext context = contexts.get(contextId);
937
+ if (context == null) {
938
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
939
+ return;
940
+ }
941
+ try {
942
+ Map<String, Object> result = loadSessionNative(context.getNativeContextId(), filepath);
943
+ if (result != null) {
944
+ callback.onResult(LlamaResult.success(result));
945
+ } else {
946
+ callback.onResult(LlamaResult.failure(new LlamaError("loadSession returned null")));
947
+ }
948
+ } catch (Exception e) {
949
+ Log.e(TAG, "loadSession failed: " + e.getMessage());
950
+ callback.onResult(LlamaResult.failure(new LlamaError("loadSession failed: " + e.getMessage())));
951
+ }
952
+ }
953
+
954
+ public void saveSession(int contextId, String filepath, int size, LlamaCallback<Integer> callback) {
955
+ LlamaContext context = contexts.get(contextId);
956
+ if (context == null) {
957
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
958
+ return;
959
+ }
960
+ try {
961
+ int tokensSaved = saveSessionNative(context.getNativeContextId(), filepath, size);
962
+ callback.onResult(LlamaResult.success(tokensSaved));
963
+ } catch (Exception e) {
964
+ Log.e(TAG, "saveSession failed: " + e.getMessage());
965
+ callback.onResult(LlamaResult.failure(new LlamaError("saveSession failed: " + e.getMessage())));
966
+ }
967
+ }
968
+
969
+ // MARK: - Tokenization
970
+
971
+ public void tokenize(int contextId, String text, String[] imagePaths, LlamaCallback<Map<String, Object>> callback) {
972
+ LlamaContext context = contexts.get(contextId);
973
+ if (context == null) {
974
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
975
+ return;
976
+ }
977
+
978
+ try {
979
+ Log.i(TAG, "Tokenizing text: " + text);
980
+
981
+ // Call native tokenization
982
+ Map<String, Object> result = tokenizeNative(context.getNativeContextId(), text, imagePaths);
983
+
984
+ if (result != null) {
985
+ Log.i(TAG, "Tokenization completed successfully");
986
+ callback.onResult(LlamaResult.success(result));
987
+ } else {
988
+ Log.e(TAG, "Tokenization returned null result");
989
+ callback.onResult(LlamaResult.failure(new LlamaError("Tokenization failed")));
990
+ }
991
+
992
+ } catch (Exception e) {
993
+ Log.e(TAG, "Tokenization failed: " + e.getMessage());
994
+ callback.onResult(LlamaResult.failure(new LlamaError("Tokenization failed: " + e.getMessage())));
995
+ }
996
+ }
997
+
998
+ public void detokenize(int contextId, Integer[] tokens, LlamaCallback<String> callback) {
999
+ LlamaContext context = contexts.get(contextId);
1000
+ if (context == null) {
1001
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1002
+ return;
1003
+ }
1004
+
1005
+ try {
1006
+ // Convert Integer[] to int[]
1007
+ int[] tokenArray = new int[tokens.length];
1008
+ for (int i = 0; i < tokens.length; i++) {
1009
+ tokenArray[i] = tokens[i];
1010
+ }
1011
+
1012
+ String result = detokenizeNative(context.getNativeContextId(), tokenArray);
1013
+ callback.onResult(LlamaResult.success(result));
1014
+
1015
+ } catch (Exception e) {
1016
+ Log.e(TAG, "Detokenization failed: " + e.getMessage());
1017
+ callback.onResult(LlamaResult.failure(new LlamaError("Detokenization failed: " + e.getMessage())));
1018
+ }
1019
+ }
1020
+
1021
+ // MARK: - Embeddings and reranking
1022
+
1023
+ public void embedding(int contextId, String text, JSObject params, LlamaCallback<Map<String, Object>> callback) {
1024
+ LlamaContext context = contexts.get(contextId);
1025
+ if (context == null) {
1026
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1027
+ return;
1028
+ }
1029
+
1030
+ try {
1031
+ Log.i(TAG, "Generating embeddings for text: " + text.substring(0, Math.min(50, text.length())));
1032
+
1033
+ // Call native embedding method
1034
+ Map<String, Object> result = embeddingNative(context.getNativeContextId(), text, params);
1035
+
1036
+ if (result != null && result.containsKey("embedding")) {
1037
+ Log.i(TAG, "Embedding generated successfully, size: " +
1038
+ (result.get("embedding") instanceof List ? ((List<?>) result.get("embedding")).size() : 0));
1039
+ callback.onResult(LlamaResult.success(result));
1040
+ } else {
1041
+ Log.e(TAG, "Embedding returned null or invalid result");
1042
+ callback.onResult(LlamaResult.failure(new LlamaError("Failed to generate embeddings")));
1043
+ }
1044
+
1045
+ } catch (Exception e) {
1046
+ Log.e(TAG, "Error generating embeddings: " + e.getMessage());
1047
+ callback.onResult(LlamaResult.failure(new LlamaError("Embedding failed: " + e.getMessage())));
1048
+ }
1049
+ }
1050
+
1051
+ public void rerank(int contextId, String query, String[] documents, JSObject params, LlamaCallback<List<Map<String, Object>>> callback) {
1052
+ LlamaContext context = contexts.get(contextId);
1053
+ if (context == null) {
1054
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1055
+ return;
1056
+ }
1057
+ try {
1058
+ List<Map<String, Object>> results = rerankNative(context.getNativeContextId(), query, documents, params);
1059
+ if (results != null) {
1060
+ callback.onResult(LlamaResult.success(results));
1061
+ } else {
1062
+ callback.onResult(LlamaResult.failure(new LlamaError("rerank returned null")));
1063
+ }
1064
+ } catch (Exception e) {
1065
+ Log.e(TAG, "rerank failed: " + e.getMessage());
1066
+ callback.onResult(LlamaResult.failure(new LlamaError("rerank failed: " + e.getMessage())));
1067
+ }
1068
+ }
1069
+
1070
+ // MARK: - Benchmarking
1071
+
1072
+ public void bench(int contextId, int pp, int tg, int pl, int nr, LlamaCallback<String> callback) {
1073
+ LlamaContext context = contexts.get(contextId);
1074
+ if (context == null) {
1075
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1076
+ return;
1077
+ }
1078
+ try {
1079
+ String result = benchNative(context.getNativeContextId(), pp, tg, pl, nr);
1080
+ callback.onResult(LlamaResult.success(result != null ? result : "[]"));
1081
+ } catch (Exception e) {
1082
+ Log.e(TAG, "bench failed: " + e.getMessage());
1083
+ callback.onResult(LlamaResult.failure(new LlamaError("bench failed: " + e.getMessage())));
1084
+ }
1085
+ }
1086
+
1087
+ // MARK: - LoRA adapters
1088
+
1089
+ public void applyLoraAdapters(int contextId, List<Map<String, Object>> loraAdapters, LlamaCallback<Void> callback) {
1090
+ LlamaContext context = contexts.get(contextId);
1091
+ if (context == null) {
1092
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1093
+ return;
1094
+ }
1095
+ try {
1096
+ // Convert List<Map> to Object[] of HashMap for JNI (jni-lora.cpp expects HashMap[])
1097
+ Object[] adaptersArray = new Object[loraAdapters.size()];
1098
+ for (int i = 0; i < loraAdapters.size(); i++) {
1099
+ java.util.HashMap<String, Object> map = new java.util.HashMap<>(loraAdapters.get(i));
1100
+ adaptersArray[i] = map;
1101
+ }
1102
+ applyLoraAdaptersNative(context.getNativeContextId(), adaptersArray);
1103
+ callback.onResult(LlamaResult.success(null));
1104
+ } catch (Exception e) {
1105
+ Log.e(TAG, "applyLoraAdapters failed: " + e.getMessage());
1106
+ callback.onResult(LlamaResult.failure(new LlamaError("applyLoraAdapters failed: " + e.getMessage())));
1107
+ }
1108
+ }
1109
+
1110
+ public void removeLoraAdapters(int contextId, LlamaCallback<Void> callback) {
1111
+ LlamaContext context = contexts.get(contextId);
1112
+ if (context == null) {
1113
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1114
+ return;
1115
+ }
1116
+ try {
1117
+ removeLoraAdaptersNative(context.getNativeContextId());
1118
+ callback.onResult(LlamaResult.success(null));
1119
+ } catch (Exception e) {
1120
+ Log.e(TAG, "removeLoraAdapters failed: " + e.getMessage());
1121
+ callback.onResult(LlamaResult.failure(new LlamaError("removeLoraAdapters failed: " + e.getMessage())));
1122
+ }
1123
+ }
1124
+
1125
+ public void getLoadedLoraAdapters(int contextId, LlamaCallback<List<Map<String, Object>>> callback) {
1126
+ LlamaContext context = contexts.get(contextId);
1127
+ if (context == null) {
1128
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1129
+ return;
1130
+ }
1131
+ try {
1132
+ List<Map<String, Object>> adapters = getLoadedLoraAdaptersNative(context.getNativeContextId());
1133
+ callback.onResult(LlamaResult.success(adapters != null ? adapters : new ArrayList<>()));
1134
+ } catch (Exception e) {
1135
+ Log.e(TAG, "getLoadedLoraAdapters failed: " + e.getMessage());
1136
+ callback.onResult(LlamaResult.failure(new LlamaError("getLoadedLoraAdapters failed: " + e.getMessage())));
1137
+ }
1138
+ }
1139
+
1140
+ // MARK: - Multimodal methods
1141
+
1142
+ public void initMultimodal(int contextId, String path, boolean useGpu, LlamaCallback<Boolean> callback) {
1143
+ LlamaContext context = contexts.get(contextId);
1144
+ if (context == null) {
1145
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1146
+ return;
1147
+ }
1148
+ try {
1149
+ boolean result = initMultimodalNative(context.getNativeContextId(), path, useGpu);
1150
+ if (result) context.setMultimodalEnabled(true);
1151
+ callback.onResult(LlamaResult.success(result));
1152
+ } catch (Exception e) {
1153
+ Log.e(TAG, "initMultimodal failed: " + e.getMessage());
1154
+ callback.onResult(LlamaResult.failure(new LlamaError("initMultimodal failed: " + e.getMessage())));
1155
+ }
1156
+ }
1157
+
1158
+ public void isMultimodalEnabled(int contextId, LlamaCallback<Boolean> callback) {
1159
+ LlamaContext context = contexts.get(contextId);
1160
+ if (context == null) {
1161
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1162
+ return;
1163
+ }
1164
+ try {
1165
+ boolean enabled = isMultimodalEnabledNative(context.getNativeContextId());
1166
+ callback.onResult(LlamaResult.success(enabled));
1167
+ } catch (Exception e) {
1168
+ callback.onResult(LlamaResult.success(context.isMultimodalEnabled()));
1169
+ }
1170
+ }
1171
+
1172
+ public void getMultimodalSupport(int contextId, LlamaCallback<Map<String, Object>> callback) {
1173
+ LlamaContext context = contexts.get(contextId);
1174
+ if (context == null) {
1175
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1176
+ return;
1177
+ }
1178
+ try {
1179
+ Map<String, Object> support = getMultimodalSupportNative(context.getNativeContextId());
1180
+ callback.onResult(LlamaResult.success(support != null ? support : new HashMap<>()));
1181
+ } catch (Exception e) {
1182
+ Log.e(TAG, "getMultimodalSupport failed: " + e.getMessage());
1183
+ callback.onResult(LlamaResult.failure(new LlamaError("getMultimodalSupport failed: " + e.getMessage())));
1184
+ }
1185
+ }
1186
+
1187
+ public void releaseMultimodal(int contextId, LlamaCallback<Void> callback) {
1188
+ LlamaContext context = contexts.get(contextId);
1189
+ if (context == null) {
1190
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1191
+ return;
1192
+ }
1193
+ try {
1194
+ releaseMultimodalNative(context.getNativeContextId());
1195
+ context.setMultimodalEnabled(false);
1196
+ callback.onResult(LlamaResult.success(null));
1197
+ } catch (Exception e) {
1198
+ Log.e(TAG, "releaseMultimodal failed: " + e.getMessage());
1199
+ callback.onResult(LlamaResult.failure(new LlamaError("releaseMultimodal failed: " + e.getMessage())));
1200
+ }
1201
+ }
1202
+
1203
+ // MARK: - TTS methods
1204
+
1205
+ public void initVocoder(int contextId, String path, Integer nBatch, LlamaCallback<Boolean> callback) {
1206
+ LlamaContext context = contexts.get(contextId);
1207
+ if (context == null) {
1208
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1209
+ return;
1210
+ }
1211
+ try {
1212
+ int batchSize = (nBatch != null && nBatch > 0) ? nBatch : 512;
1213
+ boolean result = initVocoderNative(context.getNativeContextId(), path, batchSize);
1214
+ if (result) context.setVocoderEnabled(true);
1215
+ callback.onResult(LlamaResult.success(result));
1216
+ } catch (Exception e) {
1217
+ Log.e(TAG, "initVocoder failed: " + e.getMessage());
1218
+ callback.onResult(LlamaResult.failure(new LlamaError("initVocoder failed: " + e.getMessage())));
1219
+ }
1220
+ }
1221
+
1222
+ public void isVocoderEnabled(int contextId, LlamaCallback<Boolean> callback) {
1223
+ LlamaContext context = contexts.get(contextId);
1224
+ if (context == null) {
1225
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1226
+ return;
1227
+ }
1228
+ try {
1229
+ boolean enabled = isVocoderEnabledNative(context.getNativeContextId());
1230
+ callback.onResult(LlamaResult.success(enabled));
1231
+ } catch (Exception e) {
1232
+ callback.onResult(LlamaResult.success(context.isVocoderEnabled()));
1233
+ }
1234
+ }
1235
+
1236
+ public void getFormattedAudioCompletion(int contextId, String speakerJsonStr, String textToSpeak, LlamaCallback<Map<String, Object>> callback) {
1237
+ LlamaContext context = contexts.get(contextId);
1238
+ if (context == null) {
1239
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1240
+ return;
1241
+ }
1242
+ try {
1243
+ Map<String, Object> result = getFormattedAudioCompletionNative(
1244
+ context.getNativeContextId(), speakerJsonStr, textToSpeak);
1245
+ callback.onResult(LlamaResult.success(result != null ? result : new HashMap<>()));
1246
+ } catch (Exception e) {
1247
+ Log.e(TAG, "getFormattedAudioCompletion failed: " + e.getMessage());
1248
+ callback.onResult(LlamaResult.failure(new LlamaError("getFormattedAudioCompletion failed: " + e.getMessage())));
1249
+ }
1250
+ }
1251
+
1252
+ public void getAudioCompletionGuideTokens(int contextId, String textToSpeak, LlamaCallback<List<Integer>> callback) {
1253
+ LlamaContext context = contexts.get(contextId);
1254
+ if (context == null) {
1255
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1256
+ return;
1257
+ }
1258
+ try {
1259
+ List<Integer> tokens = getAudioCompletionGuideTokensNative(
1260
+ context.getNativeContextId(), textToSpeak);
1261
+ callback.onResult(LlamaResult.success(tokens != null ? tokens : new ArrayList<>()));
1262
+ } catch (Exception e) {
1263
+ Log.e(TAG, "getAudioCompletionGuideTokens failed: " + e.getMessage());
1264
+ callback.onResult(LlamaResult.failure(new LlamaError("getAudioCompletionGuideTokens failed: " + e.getMessage())));
1265
+ }
1266
+ }
1267
+
1268
+ public void decodeAudioTokens(int contextId, Integer[] tokens, LlamaCallback<List<Integer>> callback) {
1269
+ LlamaContext context = contexts.get(contextId);
1270
+ if (context == null) {
1271
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1272
+ return;
1273
+ }
1274
+ try {
1275
+ int[] tokenArray = new int[tokens.length];
1276
+ for (int i = 0; i < tokens.length; i++) tokenArray[i] = tokens[i];
1277
+ List<Float> floatSamples = decodeAudioTokensNative(context.getNativeContextId(), tokenArray);
1278
+ // Convert float PCM samples to integer representation (multiply by 32767 for 16-bit range)
1279
+ List<Integer> intSamples = new ArrayList<>();
1280
+ if (floatSamples != null) {
1281
+ for (Float f : floatSamples) {
1282
+ intSamples.add((int) (f * 32767.0f));
1283
+ }
1284
+ }
1285
+ callback.onResult(LlamaResult.success(intSamples));
1286
+ } catch (Exception e) {
1287
+ Log.e(TAG, "decodeAudioTokens failed: " + e.getMessage());
1288
+ callback.onResult(LlamaResult.failure(new LlamaError("decodeAudioTokens failed: " + e.getMessage())));
1289
+ }
1290
+ }
1291
+
1292
+ public void releaseVocoder(int contextId, LlamaCallback<Void> callback) {
1293
+ LlamaContext context = contexts.get(contextId);
1294
+ if (context == null) {
1295
+ callback.onResult(LlamaResult.failure(new LlamaError("Context not found")));
1296
+ return;
1297
+ }
1298
+ try {
1299
+ releaseVocoderNative(context.getNativeContextId());
1300
+ context.setVocoderEnabled(false);
1301
+ callback.onResult(LlamaResult.success(null));
1302
+ } catch (Exception e) {
1303
+ Log.e(TAG, "releaseVocoder failed: " + e.getMessage());
1304
+ callback.onResult(LlamaResult.failure(new LlamaError("releaseVocoder failed: " + e.getMessage())));
1305
+ }
1306
+ }
1307
+
1308
+ // MARK: - Callback Interface
1309
+ public interface LlamaCallback<T> {
1310
+ void onResult(LlamaResult<T> result);
1311
+ }
1312
+
1313
+ // Add this method to get proper storage paths
1314
+ private String[] getModelSearchPaths(String filename) {
1315
+ List<String> paths = new ArrayList<>();
1316
+
1317
+ // Internal storage (always available, no permissions needed)
1318
+ File internalFilesDir = context.getFilesDir();
1319
+ paths.add(internalFilesDir.getAbsolutePath() + "/" + filename);
1320
+ paths.add(internalFilesDir.getAbsolutePath() + "/Documents/" + filename);
1321
+
1322
+ // External files directory (app-specific, no permissions needed on Android 10+)
1323
+ File externalFilesDir = context.getExternalFilesDir(null);
1324
+ if (externalFilesDir != null) {
1325
+ paths.add(externalFilesDir.getAbsolutePath() + "/" + filename);
1326
+ paths.add(externalFilesDir.getAbsolutePath() + "/Documents/" + filename);
1327
+ }
1328
+
1329
+ // External storage (requires permissions, may not be available)
1330
+ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
1331
+ File externalStorage = Environment.getExternalStorageDirectory();
1332
+ paths.add(externalStorage.getAbsolutePath() + "/Documents/" + filename);
1333
+ paths.add(externalStorage.getAbsolutePath() + "/Download/" + filename);
1334
+ paths.add(externalStorage.getAbsolutePath() + "/Downloads/" + filename);
1335
+ paths.add(externalStorage.getAbsolutePath() + "/Downloads/models/" + filename);
1336
+ }
1337
+
1338
+ return paths.toArray(new String[0]);
1339
+ }
1340
+ }