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.
- package/CHANGELOG.md +295 -0
- package/LICENSE +21 -0
- package/LlamaCpp.podspec +33 -0
- package/LlamaCppCapacitor.podspec +33 -0
- package/Package.swift +29 -0
- package/README.md +93 -0
- package/android/build.gradle +88 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/CMakeLists-arm64.txt +138 -0
- package/android/src/main/CMakeLists-x86_64.txt +141 -0
- package/android/src/main/CMakeLists.txt +138 -0
- package/android/src/main/java/ai/annadata/plugin/capacitor/LlamaCpp.java +1340 -0
- package/android/src/main/java/ai/annadata/plugin/capacitor/LlamaCppPlugin.java +814 -0
- package/android/src/main/java/ai/annadata/plugin/capacitor/ModelAdmissionController.java +214 -0
- package/android/src/main/jni-chat-session.cpp +261 -0
- package/android/src/main/jni-lora.cpp +197 -0
- package/android/src/main/jni-multimodal.cpp +167 -0
- package/android/src/main/jni-tts.cpp +290 -0
- package/android/src/main/jni-utils.h +148 -0
- package/android/src/main/jni.cpp +1808 -0
- package/android/src/main/jniLibs/arm64-v8a/libllama-cpp-arm64.so +0 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/build-native.sh +280 -0
- package/cmake/desktop-metal-embed.cmake +30 -0
- package/cmake/desktop-sources.cmake +100 -0
- package/cmake/ggml-backends.cmake +44 -0
- package/cpp/LICENSE +21 -0
- package/cpp/README.md +15 -0
- package/cpp/anyascii.c +22223 -0
- package/cpp/anyascii.h +42 -0
- package/cpp/cap-completion.cpp +942 -0
- package/cpp/cap-completion.h +127 -0
- package/cpp/cap-embedding.cpp +193 -0
- package/cpp/cap-embedding.h +35 -0
- package/cpp/cap-ios-bridge.cpp +1810 -0
- package/cpp/cap-ios-bridge.h +61 -0
- package/cpp/cap-llama.cpp +412 -0
- package/cpp/cap-llama.h +161 -0
- package/cpp/cap-mtmd.hpp +602 -0
- package/cpp/cap-native-server.cpp +1095 -0
- package/cpp/cap-native-server.h +40 -0
- package/cpp/cap-tts.cpp +591 -0
- package/cpp/cap-tts.h +59 -0
- package/cpp/cap-wasm-fs.cpp +156 -0
- package/cpp/cap-wasm-jspi.cpp +19 -0
- package/cpp/cap-wasm-jspi.h +30 -0
- package/cpp/cap-wasm-vfs.cpp +22 -0
- package/cpp/chat-parser.cpp +393 -0
- package/cpp/chat-parser.h +120 -0
- package/cpp/chat.cpp +2315 -0
- package/cpp/chat.h +221 -0
- package/cpp/common.cpp +1664 -0
- package/cpp/common.h +744 -0
- package/cpp/ggml-alloc.c +1028 -0
- package/cpp/ggml-alloc.h +76 -0
- package/cpp/ggml-backend-impl.h +255 -0
- package/cpp/ggml-backend-reg.cpp +600 -0
- package/cpp/ggml-backend.cpp +2121 -0
- package/cpp/ggml-backend.h +354 -0
- package/cpp/ggml-common.h +1878 -0
- package/cpp/ggml-cpp.h +39 -0
- package/cpp/ggml-cpu/amx/amx.cpp +221 -0
- package/cpp/ggml-cpu/amx/amx.h +8 -0
- package/cpp/ggml-cpu/amx/common.h +91 -0
- package/cpp/ggml-cpu/amx/mmq.cpp +2512 -0
- package/cpp/ggml-cpu/amx/mmq.h +10 -0
- package/cpp/ggml-cpu/arch/arm/cpu-feats.cpp +94 -0
- package/cpp/ggml-cpu/arch/arm/quants.c +3650 -0
- package/cpp/ggml-cpu/arch/arm/repack.cpp +1891 -0
- package/cpp/ggml-cpu/arch/x86/cpu-feats.cpp +327 -0
- package/cpp/ggml-cpu/arch/x86/quants.c +3820 -0
- package/cpp/ggml-cpu/arch/x86/repack.cpp +6307 -0
- package/cpp/ggml-cpu/arch-fallback.h +215 -0
- package/cpp/ggml-cpu/binary-ops.cpp +158 -0
- package/cpp/ggml-cpu/binary-ops.h +16 -0
- package/cpp/ggml-cpu/common.h +73 -0
- package/cpp/ggml-cpu/ggml-cpu-impl.h +559 -0
- package/cpp/ggml-cpu/ggml-cpu.c +3578 -0
- package/cpp/ggml-cpu/ggml-cpu.cpp +672 -0
- package/cpp/ggml-cpu/ops.cpp +10587 -0
- package/cpp/ggml-cpu/ops.h +114 -0
- package/cpp/ggml-cpu/quants.c +1193 -0
- package/cpp/ggml-cpu/quants.h +97 -0
- package/cpp/ggml-cpu/repack.cpp +1982 -0
- package/cpp/ggml-cpu/repack.h +120 -0
- package/cpp/ggml-cpu/simd-mappings.h +1184 -0
- package/cpp/ggml-cpu/traits.cpp +36 -0
- package/cpp/ggml-cpu/traits.h +38 -0
- package/cpp/ggml-cpu/unary-ops.cpp +186 -0
- package/cpp/ggml-cpu/unary-ops.h +28 -0
- package/cpp/ggml-cpu/vec.cpp +348 -0
- package/cpp/ggml-cpu/vec.h +1121 -0
- package/cpp/ggml-cpu.h +145 -0
- package/cpp/ggml-impl.h +622 -0
- package/cpp/ggml-metal-impl.h +688 -0
- package/cpp/ggml-metal.h +66 -0
- package/cpp/ggml-metal.m +6833 -0
- package/cpp/ggml-metal.metal +10754 -0
- package/cpp/ggml-opt.cpp +1093 -0
- package/cpp/ggml-opt.h +256 -0
- package/cpp/ggml-quants.c +5324 -0
- package/cpp/ggml-quants.h +106 -0
- package/cpp/ggml-threading.cpp +12 -0
- package/cpp/ggml-threading.h +14 -0
- package/cpp/ggml.c +7108 -0
- package/cpp/ggml.h +2492 -0
- package/cpp/gguf.cpp +1358 -0
- package/cpp/gguf.h +202 -0
- package/cpp/json-partial.cpp +256 -0
- package/cpp/json-partial.h +38 -0
- package/cpp/json-schema-to-grammar.cpp +985 -0
- package/cpp/json-schema-to-grammar.h +21 -0
- package/cpp/llama-adapter.cpp +388 -0
- package/cpp/llama-adapter.h +76 -0
- package/cpp/llama-arch.cpp +2355 -0
- package/cpp/llama-arch.h +499 -0
- package/cpp/llama-batch.cpp +875 -0
- package/cpp/llama-batch.h +160 -0
- package/cpp/llama-chat.cpp +783 -0
- package/cpp/llama-chat.h +65 -0
- package/cpp/llama-context.cpp +2788 -0
- package/cpp/llama-context.h +306 -0
- package/cpp/llama-cparams.cpp +5 -0
- package/cpp/llama-cparams.h +41 -0
- package/cpp/llama-cpp.h +30 -0
- package/cpp/llama-grammar.cpp +1229 -0
- package/cpp/llama-grammar.h +173 -0
- package/cpp/llama-graph.cpp +1891 -0
- package/cpp/llama-graph.h +810 -0
- package/cpp/llama-hparams.cpp +180 -0
- package/cpp/llama-hparams.h +233 -0
- package/cpp/llama-impl.cpp +167 -0
- package/cpp/llama-impl.h +61 -0
- package/cpp/llama-io.cpp +15 -0
- package/cpp/llama-io.h +35 -0
- package/cpp/llama-kv-cache-iswa.cpp +318 -0
- package/cpp/llama-kv-cache-iswa.h +135 -0
- package/cpp/llama-kv-cache.cpp +2059 -0
- package/cpp/llama-kv-cache.h +374 -0
- package/cpp/llama-kv-cells.h +491 -0
- package/cpp/llama-memory-hybrid.cpp +258 -0
- package/cpp/llama-memory-hybrid.h +137 -0
- package/cpp/llama-memory-recurrent.cpp +1146 -0
- package/cpp/llama-memory-recurrent.h +179 -0
- package/cpp/llama-memory.cpp +59 -0
- package/cpp/llama-memory.h +119 -0
- package/cpp/llama-mmap.cpp +609 -0
- package/cpp/llama-mmap.h +68 -0
- package/cpp/llama-model-loader.cpp +1166 -0
- package/cpp/llama-model-loader.h +170 -0
- package/cpp/llama-model-saver.cpp +282 -0
- package/cpp/llama-model-saver.h +37 -0
- package/cpp/llama-model.cpp +19061 -0
- package/cpp/llama-model.h +491 -0
- package/cpp/llama-sampling.cpp +2575 -0
- package/cpp/llama-sampling.h +32 -0
- package/cpp/llama-vocab.cpp +3792 -0
- package/cpp/llama-vocab.h +176 -0
- package/cpp/llama.cpp +358 -0
- package/cpp/llama.h +1373 -0
- package/cpp/log.cpp +428 -0
- package/cpp/log.h +103 -0
- package/cpp/minja/chat-template.hpp +550 -0
- package/cpp/minja/minja.hpp +3009 -0
- package/cpp/nlohmann/json.hpp +25526 -0
- package/cpp/nlohmann/json_fwd.hpp +187 -0
- package/cpp/regex-partial.cpp +204 -0
- package/cpp/regex-partial.h +56 -0
- package/cpp/sampling.cpp +579 -0
- package/cpp/sampling.h +107 -0
- package/cpp/tools/mtmd/clip-impl.h +473 -0
- package/cpp/tools/mtmd/clip.cpp +4322 -0
- package/cpp/tools/mtmd/clip.h +106 -0
- package/cpp/tools/mtmd/miniaudio/miniaudio.h +93468 -0
- package/cpp/tools/mtmd/mtmd-audio.cpp +769 -0
- package/cpp/tools/mtmd/mtmd-audio.h +47 -0
- package/cpp/tools/mtmd/mtmd-helper.cpp +460 -0
- package/cpp/tools/mtmd/mtmd-helper.h +95 -0
- package/cpp/tools/mtmd/mtmd.cpp +1066 -0
- package/cpp/tools/mtmd/mtmd.h +302 -0
- package/cpp/tools/mtmd/stb/stb_image.h +7988 -0
- package/cpp/unicode-data.cpp +7034 -0
- package/cpp/unicode-data.h +20 -0
- package/cpp/unicode.cpp +1061 -0
- package/cpp/unicode.h +68 -0
- package/cpp/vendor/cpp-httplib/httplib.cpp +16509 -0
- package/cpp/vendor/cpp-httplib/httplib.h +3883 -0
- package/desktop/electron-builder.config.cjs +157 -0
- package/desktop/entitlements.mac.plist +12 -0
- package/desktop/package.json +11 -0
- package/desktop/resolve-package-root.cjs +88 -0
- package/desktop/src/main/backend-selector.cjs +148 -0
- package/desktop/src/main/gpu-probe.cjs +142 -0
- package/desktop/src/main/index.cjs +58 -0
- package/desktop/src/main/ipc-handlers.cjs +212 -0
- package/desktop/src/main/model-store.cjs +50 -0
- package/desktop/src/main/preload.cjs +39 -0
- package/desktop/src/main/sidecar-client.cjs +76 -0
- package/desktop/src/main/sidecar-manager.cjs +364 -0
- package/dist/docs.json +14357 -0
- package/dist/esm/definitions.d.ts +731 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/desktop.d.ts +37 -0
- package/dist/esm/desktop.js +133 -0
- package/dist/esm/desktop.js.map +1 -0
- package/dist/esm/index.d.ts +200 -0
- package/dist/esm/index.js +612 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/isomorphic/desktop.runtime.d.ts +37 -0
- package/dist/esm/isomorphic/desktop.runtime.js +36 -0
- package/dist/esm/isomorphic/desktop.runtime.js.map +1 -0
- package/dist/esm/isomorphic/errors.d.ts +6 -0
- package/dist/esm/isomorphic/errors.js +9 -0
- package/dist/esm/isomorphic/errors.js.map +1 -0
- package/dist/esm/isomorphic/model.admission.d.ts +17 -0
- package/dist/esm/isomorphic/model.admission.js +27 -0
- package/dist/esm/isomorphic/model.admission.js.map +1 -0
- package/dist/esm/isomorphic/model.scheduler.d.ts +31 -0
- package/dist/esm/isomorphic/model.scheduler.js +95 -0
- package/dist/esm/isomorphic/model.scheduler.js.map +1 -0
- package/dist/esm/isomorphic/provider.desktop.d.ts +40 -0
- package/dist/esm/isomorphic/provider.desktop.js +411 -0
- package/dist/esm/isomorphic/provider.desktop.js.map +1 -0
- package/dist/esm/isomorphic/provider.factory.d.ts +2 -0
- package/dist/esm/isomorphic/provider.factory.js +16 -0
- package/dist/esm/isomorphic/provider.factory.js.map +1 -0
- package/dist/esm/isomorphic/provider.interface.d.ts +76 -0
- package/dist/esm/isomorphic/provider.interface.js +2 -0
- package/dist/esm/isomorphic/provider.interface.js.map +1 -0
- package/dist/esm/isomorphic/provider.native.d.ts +18 -0
- package/dist/esm/isomorphic/provider.native.js +173 -0
- package/dist/esm/isomorphic/provider.native.js.map +1 -0
- package/dist/esm/isomorphic/provider.web.d.ts +88 -0
- package/dist/esm/isomorphic/provider.web.js +573 -0
- package/dist/esm/isomorphic/provider.web.js.map +1 -0
- package/dist/esm/isomorphic/sidecar-sse.d.ts +14 -0
- package/dist/esm/isomorphic/sidecar-sse.js +76 -0
- package/dist/esm/isomorphic/sidecar-sse.js.map +1 -0
- package/dist/esm/isomorphic/wasmMemoryCalibration.d.ts +26 -0
- package/dist/esm/isomorphic/wasmMemoryCalibration.js +60 -0
- package/dist/esm/isomorphic/wasmMemoryCalibration.js.map +1 -0
- package/dist/esm/isomorphic/wasmMemoryPolicy.d.ts +55 -0
- package/dist/esm/isomorphic/wasmMemoryPolicy.js +93 -0
- package/dist/esm/isomorphic/wasmMemoryPolicy.js.map +1 -0
- package/dist/esm/storage/manifest.d.ts +13 -0
- package/dist/esm/storage/manifest.js +87 -0
- package/dist/esm/storage/manifest.js.map +1 -0
- package/dist/esm/storage/opfs.store.d.ts +31 -0
- package/dist/esm/storage/opfs.store.js +241 -0
- package/dist/esm/storage/opfs.store.js.map +1 -0
- package/dist/esm/web.d.ts +206 -0
- package/dist/esm/web.js +521 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/esm/workers/async-file.d.ts +13 -0
- package/dist/esm/workers/async-file.js +12 -0
- package/dist/esm/workers/async-file.js.map +1 -0
- package/dist/esm/workers/heapfs.d.ts +55 -0
- package/dist/esm/workers/heapfs.js +115 -0
- package/dist/esm/workers/heapfs.js.map +1 -0
- package/dist/esm/workers/wasm.engine.d.ts +86 -0
- package/dist/esm/workers/wasm.engine.js +504 -0
- package/dist/esm/workers/wasm.engine.js.map +1 -0
- package/dist/esm/workers/worker.protocol.d.ts +160 -0
- package/dist/esm/workers/worker.protocol.js +2 -0
- package/dist/esm/workers/worker.protocol.js.map +1 -0
- package/dist/plugin.cjs +3179 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.js +3181 -0
- package/dist/plugin.js.map +1 -0
- package/dist/wasm/llama_engine.d.ts +74 -0
- package/dist/wasm/llama_engine.js +1829 -0
- package/dist/wasm/llama_engine.wasm +0 -0
- package/dist/wasm/llama_engine_emscripten.mjs +2 -0
- package/dist/wasm/package.json +16 -0
- package/dist/workers/llm.worker.js +1226 -0
- package/dist/workers/llm.worker.js.map +7 -0
- package/extraResources/llama-wasm/llama_engine.d.ts +74 -0
- package/extraResources/llama-wasm/llama_engine.js +1829 -0
- package/extraResources/llama-wasm/llama_engine.wasm +0 -0
- package/extraResources/llama-wasm/llama_engine_emscripten.mjs +2 -0
- package/extraResources/llama-wasm/package.json +16 -0
- package/extraResources/sidecar/README.md +11 -0
- package/extraResources/sidecar/darwin-arm64 +0 -0
- package/extraResources/sidecar/darwin-x64 +0 -0
- package/ios/CMakeLists-arm64.txt +161 -0
- package/ios/CMakeLists-x86_64.txt +188 -0
- package/ios/CMakeLists.txt +161 -0
- package/ios/Frameworks/llama-cpp.framework/Info.plist +28 -0
- package/ios/Frameworks/llama-cpp.framework/llama-cpp +0 -0
- package/ios/Sources/LlamaCppCapacitor/LlamaCpp.swift +1365 -0
- package/ios/Sources/LlamaCppCapacitor/LlamaCppPlugin.swift +694 -0
- package/ios/Sources/LlamaCppCapacitor/LlamaNativeBridge.swift +349 -0
- package/ios/Sources/LlamaCppCapacitor/ModelAdmissionController.swift +177 -0
- package/ios/embed-metal-shaders.sh +24 -0
- package/ios/metal-embed.cmake +29 -0
- package/package.json +281 -0
- package/scripts/build-js-preserve-wasm.cjs +53 -0
- package/scripts/build-sidecar-linux.sh +6 -0
- package/scripts/build-sidecar-win.bat +19 -0
- package/scripts/build-sidecar.sh +184 -0
- package/scripts/embed-llama-ios-app-framework.sh +63 -0
- package/scripts/ensure-desktop-sidecar-bundle.cjs +103 -0
- package/scripts/ensure-llama-ios-xcframework.sh +108 -0
- package/scripts/fix-esm-extensions.cjs +45 -0
- package/scripts/prepare-js-dist.cjs +28 -0
- package/scripts/stage-desktop-resources.cjs +116 -0
- package/sidecar/CMakeLists.txt +192 -0
- package/sidecar/cap-sidecar-main.cpp +68 -0
- 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
|
+
}
|