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,1365 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
|
|
4
|
+
// MARK: - Numeric clamping helper (used by decodeAudioTokens)
|
|
5
|
+
private extension Comparable {
|
|
6
|
+
func clamped(to range: ClosedRange<Self>) -> Self {
|
|
7
|
+
return min(max(self, range.lowerBound), range.upperBound)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// MARK: - Native Library Integration
|
|
12
|
+
|
|
13
|
+
private enum LibraryLoader {
|
|
14
|
+
/// dlopen must target the framework Mach-O (`llama-cpp.framework/llama-cpp`), not the `.framework` directory.
|
|
15
|
+
static var llamaLibrary: UnsafeMutableRawPointer? = {
|
|
16
|
+
let fm = FileManager.default
|
|
17
|
+
var candidates: [String] = []
|
|
18
|
+
if let fw = Bundle.main.path(forResource: "llama-cpp", ofType: "framework") {
|
|
19
|
+
candidates.append((fw as NSString).appendingPathComponent("llama-cpp"))
|
|
20
|
+
}
|
|
21
|
+
if let exec = Bundle.main.executablePath {
|
|
22
|
+
let frameworks = (exec as NSString).deletingLastPathComponent + "/Frameworks/llama-cpp.framework/llama-cpp"
|
|
23
|
+
candidates.append(frameworks)
|
|
24
|
+
}
|
|
25
|
+
for path in candidates where fm.fileExists(atPath: path) {
|
|
26
|
+
guard let handle = dlopen(path, RTLD_NOW) else {
|
|
27
|
+
print("[LlamaCpp] dlopen failed for \(path): \(String(cString: dlerror()))")
|
|
28
|
+
continue
|
|
29
|
+
}
|
|
30
|
+
return handle
|
|
31
|
+
}
|
|
32
|
+
print("[LlamaCpp] llama-cpp framework binary not found; tried: \(candidates)")
|
|
33
|
+
return nil
|
|
34
|
+
}()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private var llamaLibrary: UnsafeMutableRawPointer? { LibraryLoader.llamaLibrary }
|
|
38
|
+
|
|
39
|
+
private typealias NativeInitContext = @convention(c) (UnsafePointer<CChar>?, UnsafePointer<CChar>?) -> Int64
|
|
40
|
+
private typealias NativeReleaseContext = @convention(c) (Int64) -> Void
|
|
41
|
+
private typealias NativeCompletion = @convention(c) (Int64, UnsafePointer<CChar>?) -> UnsafePointer<CChar>?
|
|
42
|
+
private typealias NativeGetContextModelJson = @convention(c) (Int64) -> UnsafePointer<CChar>?
|
|
43
|
+
private typealias NativeContextNEmbd = @convention(c) (Int64) -> Int32
|
|
44
|
+
private typealias NativeGetFormattedChat = @convention(c) (Int64, UnsafePointer<CChar>?, UnsafePointer<CChar>?, UnsafePointer<CChar>?) -> UnsafePointer<CChar>?
|
|
45
|
+
private typealias NativeToggleLog = @convention(c) (Bool) -> Bool
|
|
46
|
+
private typealias NativeEmbedding = @convention(c) (Int64, UnsafePointer<CChar>?, UnsafePointer<CChar>?) -> UnsafePointer<Float>?
|
|
47
|
+
private typealias NativeRegisterEmb = @convention(c) (Int64, UnsafeMutableRawPointer?) -> Void
|
|
48
|
+
private typealias NativeUnregisterEmb = @convention(c) (Int64) -> Void
|
|
49
|
+
private typealias NativeModelInfo = @convention(c) (UnsafePointer<CChar>?, UnsafePointer<CChar>?) -> UnsafePointer<CChar>?
|
|
50
|
+
private typealias NativeTokenize = @convention(c) (Int64, UnsafePointer<CChar>?, UnsafePointer<CChar>?) -> UnsafePointer<CChar>?
|
|
51
|
+
private typealias NativeDetokenize = @convention(c) (Int64, UnsafePointer<CChar>?) -> UnsafePointer<CChar>?
|
|
52
|
+
private typealias NativeGrammar = @convention(c) (UnsafePointer<CChar>?) -> UnsafePointer<CChar>?
|
|
53
|
+
private typealias NativeCapServerStart = @convention(c) (UnsafePointer<CChar>?, UnsafePointer<CChar>?, Int32, UnsafePointer<CChar>?) -> Int32
|
|
54
|
+
private typealias NativeCapServerStop = @convention(c) () -> Void
|
|
55
|
+
private typealias NativeCapServerIsRunning = @convention(c) () -> Int32
|
|
56
|
+
|
|
57
|
+
private var initContextFunc: NativeInitContext?
|
|
58
|
+
private var releaseContextFunc: NativeReleaseContext?
|
|
59
|
+
private var completionFunc: NativeCompletion?
|
|
60
|
+
private var getContextModelJsonFunc: NativeGetContextModelJson?
|
|
61
|
+
private var contextNEmbdFunc: NativeContextNEmbd?
|
|
62
|
+
private var stopCompletionFunc: NativeReleaseContext?
|
|
63
|
+
private var getFormattedChatFunc: NativeGetFormattedChat?
|
|
64
|
+
private var toggleNativeLogFunc: NativeToggleLog?
|
|
65
|
+
private var embeddingFunc: NativeEmbedding?
|
|
66
|
+
private var registerEmbeddingContextFunc: NativeRegisterEmb?
|
|
67
|
+
private var unregisterEmbeddingContextFunc: NativeUnregisterEmb?
|
|
68
|
+
private var modelInfoFunc: NativeModelInfo?
|
|
69
|
+
private var tokenizeFunc: NativeTokenize?
|
|
70
|
+
private var detokenizeFunc: NativeDetokenize?
|
|
71
|
+
private var grammarFunc: NativeGrammar?
|
|
72
|
+
private var capServerStartFunc: NativeCapServerStart?
|
|
73
|
+
private var capServerStopFunc: NativeCapServerStop?
|
|
74
|
+
private var capServerIsRunningFunc: NativeCapServerIsRunning?
|
|
75
|
+
|
|
76
|
+
private func loadFunctionPointers() {
|
|
77
|
+
guard let library = llamaLibrary else { return }
|
|
78
|
+
func sym<T>(_ name: String, _ type: T.Type) -> T? {
|
|
79
|
+
guard let p = dlsym(library, name) else { return nil }
|
|
80
|
+
return unsafeBitCast(p, to: T.self)
|
|
81
|
+
}
|
|
82
|
+
initContextFunc = sym("llama_init_context", NativeInitContext.self)
|
|
83
|
+
releaseContextFunc = sym("llama_release_context", NativeReleaseContext.self)
|
|
84
|
+
completionFunc = sym("llama_completion", NativeCompletion.self)
|
|
85
|
+
getContextModelJsonFunc = sym("llama_get_context_model_json", NativeGetContextModelJson.self)
|
|
86
|
+
contextNEmbdFunc = sym("llama_context_n_embd", NativeContextNEmbd.self)
|
|
87
|
+
stopCompletionFunc = sym("llama_stop_completion", NativeReleaseContext.self)
|
|
88
|
+
getFormattedChatFunc = sym("llama_get_formatted_chat", NativeGetFormattedChat.self)
|
|
89
|
+
toggleNativeLogFunc = sym("llama_toggle_native_log", NativeToggleLog.self)
|
|
90
|
+
embeddingFunc = sym("llama_embedding", NativeEmbedding.self)
|
|
91
|
+
registerEmbeddingContextFunc = sym("llama_embedding_register_context", NativeRegisterEmb.self)
|
|
92
|
+
unregisterEmbeddingContextFunc = sym("llama_embedding_unregister_context", NativeUnregisterEmb.self)
|
|
93
|
+
modelInfoFunc = sym("llama_model_info", NativeModelInfo.self)
|
|
94
|
+
tokenizeFunc = sym("llama_cap_tokenize", NativeTokenize.self)
|
|
95
|
+
detokenizeFunc = sym("llama_cap_detokenize", NativeDetokenize.self)
|
|
96
|
+
grammarFunc = sym("llama_convert_json_schema_to_grammar", NativeGrammar.self)
|
|
97
|
+
capServerStartFunc = sym("cap_llama_server_start", NativeCapServerStart.self)
|
|
98
|
+
capServerStopFunc = sym("cap_llama_server_stop", NativeCapServerStop.self)
|
|
99
|
+
capServerIsRunningFunc = sym("cap_llama_server_is_running", NativeCapServerIsRunning.self)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private func jsonObject(fromCString p: UnsafePointer<CChar>?) -> [String: Any]? {
|
|
103
|
+
guard let p = p else { return nil }
|
|
104
|
+
let s = String(cString: p)
|
|
105
|
+
guard let d = s.data(using: .utf8),
|
|
106
|
+
let obj = try? JSONSerialization.jsonObject(with: d) as? [String: Any] else { return nil }
|
|
107
|
+
return obj
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// MARK: - Result Types
|
|
111
|
+
typealias LlamaResult<T> = Result<T, LlamaError>
|
|
112
|
+
|
|
113
|
+
enum LlamaError: Error, LocalizedError {
|
|
114
|
+
case contextNotFound
|
|
115
|
+
case modelNotFound
|
|
116
|
+
case invalidParameters
|
|
117
|
+
case operationFailed(String)
|
|
118
|
+
case notImplemented
|
|
119
|
+
|
|
120
|
+
var errorDescription: String? {
|
|
121
|
+
switch self {
|
|
122
|
+
case .contextNotFound:
|
|
123
|
+
return "Context not found"
|
|
124
|
+
case .modelNotFound:
|
|
125
|
+
return "Model not found"
|
|
126
|
+
case .invalidParameters:
|
|
127
|
+
return "Invalid parameters"
|
|
128
|
+
case .operationFailed(let message):
|
|
129
|
+
return "Operation failed: \(message)"
|
|
130
|
+
case .notImplemented:
|
|
131
|
+
return "Operation not implemented"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// MARK: - Context Management
|
|
137
|
+
class LlamaContext {
|
|
138
|
+
let id: Int
|
|
139
|
+
var model: LlamaModel?
|
|
140
|
+
var isMultimodalEnabled: Bool = false
|
|
141
|
+
var isVocoderEnabled: Bool = false
|
|
142
|
+
|
|
143
|
+
init(id: Int) {
|
|
144
|
+
self.id = id
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
class LlamaModel {
|
|
149
|
+
let path: String
|
|
150
|
+
var desc: String
|
|
151
|
+
var size: Int
|
|
152
|
+
var nEmbd: Int
|
|
153
|
+
var nParams: Int
|
|
154
|
+
var chatTemplates: ChatTemplates
|
|
155
|
+
var metadata: [String: Any]
|
|
156
|
+
|
|
157
|
+
init(path: String, desc: String, size: Int, nEmbd: Int, nParams: Int, chatTemplates: ChatTemplates, metadata: [String: Any]) {
|
|
158
|
+
self.path = path
|
|
159
|
+
self.desc = desc
|
|
160
|
+
self.size = size
|
|
161
|
+
self.nEmbd = nEmbd
|
|
162
|
+
self.nParams = nParams
|
|
163
|
+
self.chatTemplates = chatTemplates
|
|
164
|
+
self.metadata = metadata
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
struct ChatTemplates {
|
|
169
|
+
let llamaChat: Bool
|
|
170
|
+
let minja: MinjaTemplates
|
|
171
|
+
|
|
172
|
+
init(llamaChat: Bool, minja: MinjaTemplates) {
|
|
173
|
+
self.llamaChat = llamaChat
|
|
174
|
+
self.minja = minja
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
struct MinjaTemplates {
|
|
179
|
+
let `default`: Bool
|
|
180
|
+
let defaultCaps: MinjaCaps
|
|
181
|
+
let toolUse: Bool
|
|
182
|
+
let toolUseCaps: MinjaCaps
|
|
183
|
+
|
|
184
|
+
init(default: Bool, defaultCaps: MinjaCaps, toolUse: Bool, toolUseCaps: MinjaCaps) {
|
|
185
|
+
self.default = `default`
|
|
186
|
+
self.defaultCaps = defaultCaps
|
|
187
|
+
self.toolUse = toolUse
|
|
188
|
+
self.toolUseCaps = toolUseCaps
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
struct MinjaCaps {
|
|
193
|
+
let tools: Bool
|
|
194
|
+
let toolCalls: Bool
|
|
195
|
+
let toolResponses: Bool
|
|
196
|
+
let systemRole: Bool
|
|
197
|
+
let parallelToolCalls: Bool
|
|
198
|
+
let toolCallId: Bool
|
|
199
|
+
|
|
200
|
+
init(tools: Bool, toolCalls: Bool, toolResponses: Bool, systemRole: Bool, parallelToolCalls: Bool, toolCallId: Bool) {
|
|
201
|
+
self.tools = tools
|
|
202
|
+
self.toolCalls = toolCalls
|
|
203
|
+
self.toolResponses = toolResponses
|
|
204
|
+
self.systemRole = systemRole
|
|
205
|
+
self.parallelToolCalls = parallelToolCalls
|
|
206
|
+
self.toolCallId = toolCallId
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// MARK: - Main Implementation
|
|
211
|
+
@objc public class LlamaCpp: NSObject {
|
|
212
|
+
private var contexts: [Int: LlamaContext] = [:]
|
|
213
|
+
private var nativeContexts: [Int64: UnsafeMutableRawPointer] = [:]
|
|
214
|
+
private var contextIdToNative: [Int: Int64] = [:]
|
|
215
|
+
private var contextCounter: Int = 0
|
|
216
|
+
// Default aligned with the isomorphic limit (parity with WASM_MAX_CONCURRENT_MODELS = 5).
|
|
217
|
+
private var contextLimit: Int = ModelAdmissionController.maxConcurrentModels
|
|
218
|
+
private var nativeLogEnabled: Bool = false
|
|
219
|
+
// Memory admission control (parity with the Web DefaultModelScheduler).
|
|
220
|
+
private let admissionController = ModelAdmissionController()
|
|
221
|
+
|
|
222
|
+
private func defaultMinjaCaps() -> MinjaCaps {
|
|
223
|
+
MinjaCaps(
|
|
224
|
+
tools: true,
|
|
225
|
+
toolCalls: true,
|
|
226
|
+
toolResponses: true,
|
|
227
|
+
systemRole: true,
|
|
228
|
+
parallelToolCalls: true,
|
|
229
|
+
toolCallId: true
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private func applyModelMetadata(_ model: inout LlamaModel, from m: [String: Any], defaultCaps: MinjaCaps) {
|
|
234
|
+
model.desc = (m["desc"] as? String) ?? model.desc
|
|
235
|
+
if let n = m["size"] as? NSNumber { model.size = n.intValue }
|
|
236
|
+
if let n = m["nEmbd"] as? NSNumber { model.nEmbd = n.intValue }
|
|
237
|
+
if let n = m["nParams"] as? NSNumber { model.nParams = n.intValue }
|
|
238
|
+
if let meta = m["metadata"] as? [String: Any] { model.metadata = meta }
|
|
239
|
+
if let ct = m["chatTemplates"] as? [String: Any] {
|
|
240
|
+
let llamaChat = (ct["llamaChat"] as? NSNumber)?.boolValue ?? true
|
|
241
|
+
if let minja = ct["minja"] as? [String: Any] {
|
|
242
|
+
func caps(_ d: [String: Any]?) -> MinjaCaps {
|
|
243
|
+
guard let d = d else { return defaultCaps }
|
|
244
|
+
return MinjaCaps(
|
|
245
|
+
tools: (d["tools"] as? NSNumber)?.boolValue ?? true,
|
|
246
|
+
toolCalls: (d["toolCalls"] as? NSNumber)?.boolValue ?? true,
|
|
247
|
+
toolResponses: (d["toolResponses"] as? NSNumber)?.boolValue ?? true,
|
|
248
|
+
systemRole: (d["systemRole"] as? NSNumber)?.boolValue ?? true,
|
|
249
|
+
parallelToolCalls: (d["parallelToolCalls"] as? NSNumber)?.boolValue ?? true,
|
|
250
|
+
toolCallId: (d["toolCallId"] as? NSNumber)?.boolValue ?? true
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
let defCaps = caps(minja["defaultCaps"] as? [String: Any])
|
|
254
|
+
let tuCaps = caps(minja["toolUseCaps"] as? [String: Any])
|
|
255
|
+
model.chatTemplates = ChatTemplates(
|
|
256
|
+
llamaChat: llamaChat,
|
|
257
|
+
minja: MinjaTemplates(
|
|
258
|
+
default: (minja["default"] as? NSNumber)?.boolValue ?? true,
|
|
259
|
+
defaultCaps: defCaps,
|
|
260
|
+
toolUse: (minja["toolUse"] as? NSNumber)?.boolValue ?? true,
|
|
261
|
+
toolUseCaps: tuCaps
|
|
262
|
+
)
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/// Query n_embd from native at init or embed time (Android JNI does this inline).
|
|
269
|
+
private func resolveEmbeddingDimension(context: LlamaContext, nativeId: Int64) -> Int {
|
|
270
|
+
if let cached = context.model?.nEmbd, cached > 0 {
|
|
271
|
+
return cached
|
|
272
|
+
}
|
|
273
|
+
if getContextModelJsonFunc == nil || contextNEmbdFunc == nil {
|
|
274
|
+
loadFunctionPointers()
|
|
275
|
+
}
|
|
276
|
+
if let jsonFn = getContextModelJsonFunc, let c = jsonFn(nativeId), let m = jsonObject(fromCString: c) {
|
|
277
|
+
if var model = context.model {
|
|
278
|
+
applyModelMetadata(&model, from: m, defaultCaps: defaultMinjaCaps())
|
|
279
|
+
context.model = model
|
|
280
|
+
if model.nEmbd > 0 {
|
|
281
|
+
return model.nEmbd
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if let nEmbdFn = contextNEmbdFunc {
|
|
286
|
+
let n = Int(nEmbdFn(nativeId))
|
|
287
|
+
if n > 0 {
|
|
288
|
+
if var model = context.model {
|
|
289
|
+
model.nEmbd = n
|
|
290
|
+
context.model = model
|
|
291
|
+
}
|
|
292
|
+
return n
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return 0
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// MARK: - Core initialization and management
|
|
299
|
+
|
|
300
|
+
func toggleNativeLog(enabled: Bool, completion: @escaping (LlamaResult<Void>) -> Void) {
|
|
301
|
+
nativeLogEnabled = enabled
|
|
302
|
+
if initContextFunc == nil { loadFunctionPointers() }
|
|
303
|
+
if let fn = toggleNativeLogFunc {
|
|
304
|
+
_ = fn(enabled)
|
|
305
|
+
}
|
|
306
|
+
if enabled {
|
|
307
|
+
print("[LlamaCpp] Native logging enabled")
|
|
308
|
+
} else {
|
|
309
|
+
print("[LlamaCpp] Native logging disabled")
|
|
310
|
+
}
|
|
311
|
+
completion(.success(()))
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
func setContextLimit(limit: Int, completion: @escaping (LlamaResult<Void>) -> Void) {
|
|
315
|
+
let maxAllowed = admissionController.maxModelCount
|
|
316
|
+
guard limit >= 1 else {
|
|
317
|
+
completion(.failure(.operationFailed("Context limit must be at least 1")))
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
if limit > maxAllowed {
|
|
321
|
+
// Clamp to the isomorphic hard cap rather than silently over-committing memory.
|
|
322
|
+
print("[LlamaCpp] Requested context limit \(limit) exceeds max \(maxAllowed); clamping to \(maxAllowed)")
|
|
323
|
+
contextLimit = maxAllowed
|
|
324
|
+
} else {
|
|
325
|
+
contextLimit = limit
|
|
326
|
+
}
|
|
327
|
+
print("[LlamaCpp] Context limit set to \(contextLimit)")
|
|
328
|
+
completion(.success(()))
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
func modelInfo(path: String, skip: [String], completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
332
|
+
if modelInfoFunc == nil { loadFunctionPointers() }
|
|
333
|
+
guard let fn = modelInfoFunc else {
|
|
334
|
+
completion(.failure(.operationFailed("llama_model_info not found")))
|
|
335
|
+
return
|
|
336
|
+
}
|
|
337
|
+
var skipJson = "[]"
|
|
338
|
+
if !skip.isEmpty, let d = try? JSONSerialization.data(withJSONObject: skip), let s = String(data: d, encoding: .utf8) {
|
|
339
|
+
skipJson = s
|
|
340
|
+
}
|
|
341
|
+
let result: LlamaResult<[String: Any]> = path.withCString { pathPtr in
|
|
342
|
+
skipJson.withCString { skipPtr in
|
|
343
|
+
guard let c = fn(pathPtr, skipPtr), let dict = jsonObject(fromCString: c) else {
|
|
344
|
+
return .failure(.operationFailed("model info failed"))
|
|
345
|
+
}
|
|
346
|
+
if let err = dict["error"] as? String {
|
|
347
|
+
return .failure(.operationFailed(err))
|
|
348
|
+
}
|
|
349
|
+
return .success(dict)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
completion(result)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
func initContext(contextId: Int, params: [String: Any], completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
356
|
+
// Extract parameters
|
|
357
|
+
guard let modelPath = params["model"] as? String else {
|
|
358
|
+
completion(.failure(.invalidParameters))
|
|
359
|
+
return
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Memory admission control: enforce the concurrent-model slot limit AND a
|
|
363
|
+
// process-memory guard before touching native code (parity with the Web path).
|
|
364
|
+
let embedding = (params["embedding"] as? Bool) ?? false
|
|
365
|
+
let decision = admissionController.canAdmit(
|
|
366
|
+
currentlyLoaded: contexts.count,
|
|
367
|
+
modelPath: modelPath,
|
|
368
|
+
embedding: embedding
|
|
369
|
+
)
|
|
370
|
+
if !decision.allow {
|
|
371
|
+
let prefix = decision.deniedBy == .limit ? "MODEL_LIMIT_REACHED: " : "INSUFFICIENT_MEMORY: "
|
|
372
|
+
let reason = decision.reason ?? "Model admission rejected"
|
|
373
|
+
print("[LlamaCpp] Admission rejected for context \(contextId) — \(reason)")
|
|
374
|
+
completion(.failure(.operationFailed(prefix + reason)))
|
|
375
|
+
return
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Create context
|
|
379
|
+
let context = LlamaContext(id: contextId)
|
|
380
|
+
|
|
381
|
+
let defaultCaps = defaultMinjaCaps()
|
|
382
|
+
let defaultTemplates = ChatTemplates(
|
|
383
|
+
llamaChat: true,
|
|
384
|
+
minja: MinjaTemplates(
|
|
385
|
+
default: true,
|
|
386
|
+
defaultCaps: defaultCaps,
|
|
387
|
+
toolUse: true,
|
|
388
|
+
toolUseCaps: defaultCaps
|
|
389
|
+
)
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
var model = LlamaModel(
|
|
393
|
+
path: modelPath,
|
|
394
|
+
desc: "model",
|
|
395
|
+
size: 0,
|
|
396
|
+
nEmbd: 0,
|
|
397
|
+
nParams: 0,
|
|
398
|
+
chatTemplates: defaultTemplates,
|
|
399
|
+
metadata: [:]
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
context.model = model
|
|
403
|
+
|
|
404
|
+
var paramsJson = "{}"
|
|
405
|
+
do {
|
|
406
|
+
let paramsData = try JSONSerialization.data(withJSONObject: params)
|
|
407
|
+
paramsJson = String(data: paramsData, encoding: .utf8) ?? "{}"
|
|
408
|
+
} catch {
|
|
409
|
+
completion(.failure(.operationFailed("Failed to serialize params: \(error.localizedDescription)")))
|
|
410
|
+
return
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if initContextFunc == nil {
|
|
414
|
+
loadFunctionPointers()
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
let nativeContextId: Int64
|
|
418
|
+
do {
|
|
419
|
+
nativeContextId = try LlamaNativeBridge.initContext(modelPath: modelPath, paramsJson: paramsJson)
|
|
420
|
+
} catch let error as LlamaNativeBridge.Failure {
|
|
421
|
+
completion(.failure(.operationFailed(error.localizedDescription)))
|
|
422
|
+
return
|
|
423
|
+
} catch {
|
|
424
|
+
completion(.failure(.operationFailed("Native initContext failed: \(error.localizedDescription)")))
|
|
425
|
+
return
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
guard nativeContextId > 0 else {
|
|
429
|
+
completion(.failure(.operationFailed("Failed to initialize native context")))
|
|
430
|
+
return
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
contexts[contextId] = context
|
|
434
|
+
nativeContexts[nativeContextId] = UnsafeMutableRawPointer(bitPattern: Int(truncatingIfNeeded: nativeContextId))
|
|
435
|
+
contextIdToNative[contextId] = nativeContextId
|
|
436
|
+
|
|
437
|
+
if let jsonFn = getContextModelJsonFunc, let c = jsonFn(nativeContextId), let m = jsonObject(fromCString: c) {
|
|
438
|
+
applyModelMetadata(&model, from: m, defaultCaps: defaultCaps)
|
|
439
|
+
context.model = model
|
|
440
|
+
}
|
|
441
|
+
if model.nEmbd <= 0 {
|
|
442
|
+
_ = resolveEmbeddingDimension(context: context, nativeId: nativeContextId)
|
|
443
|
+
if let refreshed = context.model {
|
|
444
|
+
model = refreshed
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Query GPU info from native layer
|
|
449
|
+
let gpuInfo = LlamaNativeBridge.queryGpuInfo(nativeContextId)
|
|
450
|
+
let gpuEnabled = gpuInfo.gpu
|
|
451
|
+
let reasonNoGPU = gpuEnabled ? "" : (gpuInfo.reason.isEmpty ? "Metal not used" : gpuInfo.reason)
|
|
452
|
+
|
|
453
|
+
// Return context info — reflect actual GPU state
|
|
454
|
+
let contextInfo: [String: Any] = [
|
|
455
|
+
"contextId": contextId,
|
|
456
|
+
"gpu": gpuEnabled,
|
|
457
|
+
"reasonNoGPU": reasonNoGPU,
|
|
458
|
+
"model": [
|
|
459
|
+
"desc": model.desc,
|
|
460
|
+
"size": model.size,
|
|
461
|
+
"nEmbd": model.nEmbd,
|
|
462
|
+
"nParams": model.nParams,
|
|
463
|
+
"chatTemplates": [
|
|
464
|
+
"llamaChat": model.chatTemplates.llamaChat,
|
|
465
|
+
"minja": [
|
|
466
|
+
"default": model.chatTemplates.minja.default,
|
|
467
|
+
"defaultCaps": [
|
|
468
|
+
"tools": model.chatTemplates.minja.defaultCaps.tools,
|
|
469
|
+
"toolCalls": model.chatTemplates.minja.defaultCaps.toolCalls,
|
|
470
|
+
"toolResponses": model.chatTemplates.minja.defaultCaps.toolResponses,
|
|
471
|
+
"systemRole": model.chatTemplates.minja.defaultCaps.systemRole,
|
|
472
|
+
"parallelToolCalls": model.chatTemplates.minja.defaultCaps.parallelToolCalls,
|
|
473
|
+
"toolCallId": model.chatTemplates.minja.defaultCaps.toolCallId
|
|
474
|
+
],
|
|
475
|
+
"toolUse": model.chatTemplates.minja.toolUse,
|
|
476
|
+
"toolUseCaps": [
|
|
477
|
+
"tools": model.chatTemplates.minja.toolUseCaps.tools,
|
|
478
|
+
"toolCalls": model.chatTemplates.minja.toolUseCaps.toolCalls,
|
|
479
|
+
"toolResponses": model.chatTemplates.minja.toolUseCaps.toolResponses,
|
|
480
|
+
"systemRole": model.chatTemplates.minja.toolUseCaps.systemRole,
|
|
481
|
+
"parallelToolCalls": model.chatTemplates.minja.toolUseCaps.parallelToolCalls,
|
|
482
|
+
"toolCallId": model.chatTemplates.minja.toolUseCaps.toolCallId
|
|
483
|
+
]
|
|
484
|
+
]
|
|
485
|
+
],
|
|
486
|
+
"metadata": model.metadata,
|
|
487
|
+
"isChatTemplateSupported": true
|
|
488
|
+
]
|
|
489
|
+
]
|
|
490
|
+
|
|
491
|
+
completion(.success(contextInfo))
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
func releaseContext(contextId: Int, completion: @escaping (LlamaResult<Void>) -> Void) {
|
|
495
|
+
guard contexts[contextId] != nil else {
|
|
496
|
+
completion(.failure(.contextNotFound))
|
|
497
|
+
return
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
let nativeId = contextIdToNative[contextId] ?? Int64(contextId)
|
|
501
|
+
|
|
502
|
+
LlamaNativeBridge.releaseContext(nativeId)
|
|
503
|
+
|
|
504
|
+
contexts.removeValue(forKey: contextId)
|
|
505
|
+
nativeContexts.removeValue(forKey: nativeId)
|
|
506
|
+
contextIdToNative.removeValue(forKey: contextId)
|
|
507
|
+
completion(.success(()))
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
func releaseAllContexts(completion: @escaping (LlamaResult<Void>) -> Void) {
|
|
511
|
+
let pairs = Array(contextIdToNative)
|
|
512
|
+
for (_, nativeId) in pairs {
|
|
513
|
+
LlamaNativeBridge.releaseContext(nativeId)
|
|
514
|
+
}
|
|
515
|
+
contexts.removeAll()
|
|
516
|
+
nativeContexts.removeAll()
|
|
517
|
+
contextIdToNative.removeAll()
|
|
518
|
+
completion(.success(()))
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// MARK: - Chat and completion
|
|
522
|
+
|
|
523
|
+
func getFormattedChat(contextId: Int, messages: String, chatTemplate: String?, params: [String: Any]?, completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
524
|
+
guard contexts[contextId] != nil else {
|
|
525
|
+
completion(.failure(.contextNotFound))
|
|
526
|
+
return
|
|
527
|
+
}
|
|
528
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
529
|
+
completion(.failure(.contextNotFound))
|
|
530
|
+
return
|
|
531
|
+
}
|
|
532
|
+
if getFormattedChatFunc == nil { loadFunctionPointers() }
|
|
533
|
+
guard let fn = getFormattedChatFunc else {
|
|
534
|
+
completion(.failure(.operationFailed("llama_get_formatted_chat not found")))
|
|
535
|
+
return
|
|
536
|
+
}
|
|
537
|
+
var paramsJson = "{}"
|
|
538
|
+
if let p = params, let d = try? JSONSerialization.data(withJSONObject: p), let s = String(data: d, encoding: .utf8) {
|
|
539
|
+
paramsJson = s
|
|
540
|
+
}
|
|
541
|
+
let template = chatTemplate ?? ""
|
|
542
|
+
let result: LlamaResult<[String: Any]> = messages.withCString { msgPtr in
|
|
543
|
+
template.withCString { tplPtr in
|
|
544
|
+
paramsJson.withCString { parPtr in
|
|
545
|
+
guard let c = fn(nativeId, msgPtr, tplPtr, parPtr), let dict = jsonObject(fromCString: c) else {
|
|
546
|
+
return .failure(.operationFailed("formatted chat failed"))
|
|
547
|
+
}
|
|
548
|
+
if let err = dict["error"] as? String {
|
|
549
|
+
return .failure(.operationFailed(err))
|
|
550
|
+
}
|
|
551
|
+
return .success(dict)
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
completion(result)
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
func completion(contextId: Int, params: [String: Any], completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
559
|
+
guard contexts[contextId] != nil else {
|
|
560
|
+
completion(.failure(.contextNotFound))
|
|
561
|
+
return
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
let nativeId = contextIdToNative[contextId] ?? Int64(contextId)
|
|
565
|
+
var paramsJson = "{}"
|
|
566
|
+
do {
|
|
567
|
+
let paramsData = try JSONSerialization.data(withJSONObject: params)
|
|
568
|
+
paramsJson = String(data: paramsData, encoding: .utf8) ?? "{}"
|
|
569
|
+
} catch {
|
|
570
|
+
completion(.failure(.operationFailed("Failed to serialize params: \(error.localizedDescription)")))
|
|
571
|
+
return
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
575
|
+
do {
|
|
576
|
+
let completionResult = try LlamaNativeBridge.runCompletion(contextId: nativeId, paramsJson: paramsJson)
|
|
577
|
+
completion(.success(completionResult))
|
|
578
|
+
} catch let error as LlamaNativeBridge.Failure {
|
|
579
|
+
completion(.failure(.operationFailed(error.localizedDescription)))
|
|
580
|
+
} catch {
|
|
581
|
+
completion(.failure(.operationFailed("Native completion failed: \(error.localizedDescription)")))
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
func stopCompletion(contextId: Int, completion: @escaping (LlamaResult<Void>) -> Void) {
|
|
587
|
+
guard contexts[contextId] != nil else {
|
|
588
|
+
completion(.failure(.contextNotFound))
|
|
589
|
+
return
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
let nativeId = contextIdToNative[contextId] ?? Int64(contextId)
|
|
593
|
+
LlamaNativeBridge.stopCompletion(nativeId)
|
|
594
|
+
completion(.success(()))
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// MARK: - Chat-first methods (like llama-cli -sys)
|
|
598
|
+
|
|
599
|
+
func chat(contextId: Int, messages: [JSObject], system: String?, chatTemplate: String?, params: [String: Any]?, completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
600
|
+
guard contexts[contextId] != nil else {
|
|
601
|
+
completion(.failure(.contextNotFound))
|
|
602
|
+
return
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
do {
|
|
606
|
+
// Convert JSObject messages to JSON string
|
|
607
|
+
let messagesData = try JSONSerialization.data(withJSONObject: messages)
|
|
608
|
+
let messagesJson = String(data: messagesData, encoding: .utf8) ?? "[]"
|
|
609
|
+
|
|
610
|
+
// Add system message if provided
|
|
611
|
+
var allMessages = messages
|
|
612
|
+
if let system = system, !system.isEmpty {
|
|
613
|
+
let systemMessage: [String: Any] = [
|
|
614
|
+
"role": "system",
|
|
615
|
+
"content": system
|
|
616
|
+
]
|
|
617
|
+
let jsSystem = JSTypes.coerceDictionaryToJSObject(systemMessage) ?? [:]
|
|
618
|
+
allMessages.insert(jsSystem, at: 0)
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Convert to JSON string for getFormattedChat
|
|
622
|
+
let allMessagesData = try JSONSerialization.data(withJSONObject: allMessages)
|
|
623
|
+
let allMessagesJson = String(data: allMessagesData, encoding: .utf8) ?? "[]"
|
|
624
|
+
|
|
625
|
+
// First, format the chat
|
|
626
|
+
getFormattedChat(contextId: contextId, messages: allMessagesJson, chatTemplate: chatTemplate, params: nil) { [weak self] result in
|
|
627
|
+
switch result {
|
|
628
|
+
case .success(let formattedResult):
|
|
629
|
+
// Extract the formatted prompt
|
|
630
|
+
let formattedPrompt = formattedResult["prompt"] as? String ?? ""
|
|
631
|
+
|
|
632
|
+
// Create completion parameters
|
|
633
|
+
var completionParams = params ?? [:]
|
|
634
|
+
completionParams["prompt"] = formattedPrompt
|
|
635
|
+
|
|
636
|
+
// Call completion with formatted prompt
|
|
637
|
+
self?.completion(contextId: contextId, params: completionParams, completion: completion)
|
|
638
|
+
|
|
639
|
+
case .failure(let error):
|
|
640
|
+
completion(.failure(error))
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
} catch {
|
|
645
|
+
completion(.failure(.contextNotFound)) // Use a more appropriate error
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
func chatWithSystem(contextId: Int, system: String, message: String, params: [String: Any]?, completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
650
|
+
// Create a simple message array
|
|
651
|
+
let userMessage: [String: Any] = [
|
|
652
|
+
"role": "user",
|
|
653
|
+
"content": message
|
|
654
|
+
]
|
|
655
|
+
let jsUser = JSTypes.coerceDictionaryToJSObject(userMessage) ?? [:]
|
|
656
|
+
let messages: [JSObject] = [jsUser]
|
|
657
|
+
|
|
658
|
+
// Call the main chat method
|
|
659
|
+
chat(contextId: contextId, messages: messages, system: system, chatTemplate: nil, params: params, completion: completion)
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
func generateText(contextId: Int, prompt: String, params: [String: Any]?, completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
663
|
+
guard contexts[contextId] != nil else {
|
|
664
|
+
completion(.failure(.contextNotFound))
|
|
665
|
+
return
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Create completion parameters
|
|
669
|
+
var completionParams = params ?? [:]
|
|
670
|
+
completionParams["prompt"] = prompt
|
|
671
|
+
|
|
672
|
+
// Call completion method directly
|
|
673
|
+
self.completion(contextId: contextId, params: completionParams, completion: completion)
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// MARK: - Session management
|
|
677
|
+
|
|
678
|
+
func loadSession(contextId: Int, filepath: String, completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
679
|
+
guard contexts[contextId] != nil else {
|
|
680
|
+
completion(.failure(.contextNotFound))
|
|
681
|
+
return
|
|
682
|
+
}
|
|
683
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
684
|
+
completion(.failure(.contextNotFound))
|
|
685
|
+
return
|
|
686
|
+
}
|
|
687
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
688
|
+
do {
|
|
689
|
+
let result = try LlamaNativeBridge.loadSession(contextId: nativeId, filepath: filepath)
|
|
690
|
+
completion(.success(result))
|
|
691
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
692
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
693
|
+
} catch {
|
|
694
|
+
completion(.failure(.operationFailed("loadSession failed: \(error.localizedDescription)")))
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
func saveSession(contextId: Int, filepath: String, size: Int, completion: @escaping (LlamaResult<Int>) -> Void) {
|
|
700
|
+
guard contexts[contextId] != nil else {
|
|
701
|
+
completion(.failure(.contextNotFound))
|
|
702
|
+
return
|
|
703
|
+
}
|
|
704
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
705
|
+
completion(.failure(.contextNotFound))
|
|
706
|
+
return
|
|
707
|
+
}
|
|
708
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
709
|
+
do {
|
|
710
|
+
let saved = try LlamaNativeBridge.saveSession(contextId: nativeId, filepath: filepath, size: size)
|
|
711
|
+
completion(.success(saved))
|
|
712
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
713
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
714
|
+
} catch {
|
|
715
|
+
completion(.failure(.operationFailed("saveSession failed: \(error.localizedDescription)")))
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// MARK: - Tokenization
|
|
721
|
+
|
|
722
|
+
func tokenize(contextId: Int, text: String, imagePaths: [String], completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
723
|
+
guard contexts[contextId] != nil else {
|
|
724
|
+
completion(.failure(.contextNotFound))
|
|
725
|
+
return
|
|
726
|
+
}
|
|
727
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
728
|
+
completion(.failure(.contextNotFound))
|
|
729
|
+
return
|
|
730
|
+
}
|
|
731
|
+
if tokenizeFunc == nil { loadFunctionPointers() }
|
|
732
|
+
guard let fn = tokenizeFunc else {
|
|
733
|
+
completion(.failure(.operationFailed("llama_cap_tokenize not found")))
|
|
734
|
+
return
|
|
735
|
+
}
|
|
736
|
+
var pathsJson = "[]"
|
|
737
|
+
if !imagePaths.isEmpty, let d = try? JSONSerialization.data(withJSONObject: imagePaths), let s = String(data: d, encoding: .utf8) {
|
|
738
|
+
pathsJson = s
|
|
739
|
+
}
|
|
740
|
+
let result: LlamaResult<[String: Any]> = text.withCString { txtPtr in
|
|
741
|
+
pathsJson.withCString { pj in
|
|
742
|
+
guard let c = fn(nativeId, txtPtr, pj), let dict = jsonObject(fromCString: c) else {
|
|
743
|
+
return .failure(.operationFailed("tokenize failed"))
|
|
744
|
+
}
|
|
745
|
+
if let err = dict["error"] as? String {
|
|
746
|
+
return .failure(.operationFailed(err))
|
|
747
|
+
}
|
|
748
|
+
return .success(dict)
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
completion(result)
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
func detokenize(contextId: Int, tokens: [Int], completion: @escaping (LlamaResult<String>) -> Void) {
|
|
755
|
+
guard contexts[contextId] != nil else {
|
|
756
|
+
completion(.failure(.contextNotFound))
|
|
757
|
+
return
|
|
758
|
+
}
|
|
759
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
760
|
+
completion(.failure(.contextNotFound))
|
|
761
|
+
return
|
|
762
|
+
}
|
|
763
|
+
if detokenizeFunc == nil { loadFunctionPointers() }
|
|
764
|
+
guard let fn = detokenizeFunc else {
|
|
765
|
+
completion(.failure(.operationFailed("llama_cap_detokenize not found")))
|
|
766
|
+
return
|
|
767
|
+
}
|
|
768
|
+
guard let d = try? JSONSerialization.data(withJSONObject: tokens), let tokensJson = String(data: d, encoding: .utf8) else {
|
|
769
|
+
completion(.failure(.invalidParameters))
|
|
770
|
+
return
|
|
771
|
+
}
|
|
772
|
+
let s: LlamaResult<String> = tokensJson.withCString { ptr in
|
|
773
|
+
guard let c = fn(nativeId, ptr) else {
|
|
774
|
+
return .success("")
|
|
775
|
+
}
|
|
776
|
+
return .success(String(cString: c))
|
|
777
|
+
}
|
|
778
|
+
completion(s)
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// MARK: - Embeddings and reranking
|
|
782
|
+
|
|
783
|
+
func embedding(contextId: Int, text: String, params: [String: Any], completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
784
|
+
guard contexts[contextId] != nil else {
|
|
785
|
+
completion(.failure(.contextNotFound))
|
|
786
|
+
return
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
let nativeId = contextIdToNative[contextId] ?? Int64(contextId)
|
|
790
|
+
var paramsJson = "{}"
|
|
791
|
+
if !params.isEmpty {
|
|
792
|
+
do {
|
|
793
|
+
let paramsData = try JSONSerialization.data(withJSONObject: params)
|
|
794
|
+
paramsJson = String(data: paramsData, encoding: .utf8) ?? "{}"
|
|
795
|
+
} catch {
|
|
796
|
+
completion(.failure(.operationFailed("Failed to serialize params: \(error.localizedDescription)")))
|
|
797
|
+
return
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
802
|
+
do {
|
|
803
|
+
let embeddingResult = try LlamaNativeBridge.runEmbedding(contextId: nativeId, text: text, paramsJson: paramsJson)
|
|
804
|
+
completion(.success(embeddingResult))
|
|
805
|
+
} catch let error as LlamaNativeBridge.Failure {
|
|
806
|
+
completion(.failure(.operationFailed(error.localizedDescription)))
|
|
807
|
+
} catch {
|
|
808
|
+
completion(.failure(.operationFailed("Native embedding failed: \(error.localizedDescription)")))
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
func rerank(contextId: Int, query: String, documents: [String], params: [String: Any]?, completion: @escaping (LlamaResult<[[String: Any]]>) -> Void) {
|
|
814
|
+
guard contexts[contextId] != nil else {
|
|
815
|
+
completion(.failure(.contextNotFound))
|
|
816
|
+
return
|
|
817
|
+
}
|
|
818
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
819
|
+
completion(.failure(.contextNotFound))
|
|
820
|
+
return
|
|
821
|
+
}
|
|
822
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
823
|
+
do {
|
|
824
|
+
guard let docsData = try? JSONSerialization.data(withJSONObject: documents),
|
|
825
|
+
let docsJson = String(data: docsData, encoding: .utf8) else {
|
|
826
|
+
completion(.failure(.operationFailed("Failed to serialize documents")))
|
|
827
|
+
return
|
|
828
|
+
}
|
|
829
|
+
let results = try LlamaNativeBridge.runRerank(contextId: nativeId, query: query, documentsJson: docsJson)
|
|
830
|
+
completion(.success(results))
|
|
831
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
832
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
833
|
+
} catch {
|
|
834
|
+
completion(.failure(.operationFailed("rerank failed: \(error.localizedDescription)")))
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
func bench(contextId: Int, pp: Int, tg: Int, pl: Int, nr: Int, completion: @escaping (LlamaResult<String>) -> Void) {
|
|
840
|
+
guard contexts[contextId] != nil else {
|
|
841
|
+
completion(.failure(.contextNotFound))
|
|
842
|
+
return
|
|
843
|
+
}
|
|
844
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
845
|
+
completion(.failure(.contextNotFound))
|
|
846
|
+
return
|
|
847
|
+
}
|
|
848
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
849
|
+
do {
|
|
850
|
+
let result = try LlamaNativeBridge.runBench(contextId: nativeId, pp: pp, tg: tg, pl: pl, nr: nr)
|
|
851
|
+
completion(.success(result))
|
|
852
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
853
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
854
|
+
} catch {
|
|
855
|
+
completion(.failure(.operationFailed("bench failed: \(error.localizedDescription)")))
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// MARK: - LoRA adapters
|
|
861
|
+
|
|
862
|
+
func applyLoraAdapters(contextId: Int, loraAdapters: [[String: Any]], completion: @escaping (LlamaResult<Void>) -> Void) {
|
|
863
|
+
guard contexts[contextId] != nil else {
|
|
864
|
+
completion(.failure(.contextNotFound))
|
|
865
|
+
return
|
|
866
|
+
}
|
|
867
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
868
|
+
completion(.failure(.contextNotFound))
|
|
869
|
+
return
|
|
870
|
+
}
|
|
871
|
+
do {
|
|
872
|
+
let data = try JSONSerialization.data(withJSONObject: loraAdapters)
|
|
873
|
+
let json = String(data: data, encoding: .utf8) ?? "[]"
|
|
874
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
875
|
+
do {
|
|
876
|
+
_ = try LlamaNativeBridge.applyLoraAdapters(contextId: nativeId, loraAdaptersJson: json)
|
|
877
|
+
completion(.success(()))
|
|
878
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
879
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
880
|
+
} catch {
|
|
881
|
+
completion(.failure(.operationFailed("applyLoraAdapters failed: \(error.localizedDescription)")))
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
} catch {
|
|
885
|
+
completion(.failure(.operationFailed("Failed to serialize LoRA adapters: \(error.localizedDescription)")))
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
func removeLoraAdapters(contextId: Int, completion: @escaping (LlamaResult<Void>) -> Void) {
|
|
890
|
+
guard contexts[contextId] != nil else {
|
|
891
|
+
completion(.failure(.contextNotFound))
|
|
892
|
+
return
|
|
893
|
+
}
|
|
894
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
895
|
+
completion(.failure(.contextNotFound))
|
|
896
|
+
return
|
|
897
|
+
}
|
|
898
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
899
|
+
do {
|
|
900
|
+
try LlamaNativeBridge.removeLoraAdapters(contextId: nativeId)
|
|
901
|
+
completion(.success(()))
|
|
902
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
903
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
904
|
+
} catch {
|
|
905
|
+
completion(.failure(.operationFailed("removeLoraAdapters failed: \(error.localizedDescription)")))
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
func getLoadedLoraAdapters(contextId: Int, completion: @escaping (LlamaResult<[[String: Any]]>) -> Void) {
|
|
911
|
+
guard contexts[contextId] != nil else {
|
|
912
|
+
completion(.failure(.contextNotFound))
|
|
913
|
+
return
|
|
914
|
+
}
|
|
915
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
916
|
+
completion(.failure(.contextNotFound))
|
|
917
|
+
return
|
|
918
|
+
}
|
|
919
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
920
|
+
do {
|
|
921
|
+
let adapters = try LlamaNativeBridge.getLoadedLoraAdapters(contextId: nativeId)
|
|
922
|
+
completion(.success(adapters))
|
|
923
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
924
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
925
|
+
} catch {
|
|
926
|
+
completion(.failure(.operationFailed("getLoadedLoraAdapters failed: \(error.localizedDescription)")))
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// MARK: - Multimodal methods
|
|
932
|
+
|
|
933
|
+
func initMultimodal(contextId: Int, path: String, useGpu: Bool, completion: @escaping (LlamaResult<Bool>) -> Void) {
|
|
934
|
+
guard let context = contexts[contextId] else {
|
|
935
|
+
completion(.failure(.contextNotFound))
|
|
936
|
+
return
|
|
937
|
+
}
|
|
938
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
939
|
+
completion(.failure(.contextNotFound))
|
|
940
|
+
return
|
|
941
|
+
}
|
|
942
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
943
|
+
do {
|
|
944
|
+
let ok = try LlamaNativeBridge.initMultimodal(contextId: nativeId, mmProjPath: path, useGpu: useGpu)
|
|
945
|
+
if ok { context.isMultimodalEnabled = true }
|
|
946
|
+
completion(.success(ok))
|
|
947
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
948
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
949
|
+
} catch {
|
|
950
|
+
completion(.failure(.operationFailed("initMultimodal failed: \(error.localizedDescription)")))
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
func isMultimodalEnabled(contextId: Int, completion: @escaping (LlamaResult<Bool>) -> Void) {
|
|
956
|
+
guard let context = contexts[contextId] else {
|
|
957
|
+
completion(.failure(.contextNotFound))
|
|
958
|
+
return
|
|
959
|
+
}
|
|
960
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
961
|
+
completion(.success(context.isMultimodalEnabled))
|
|
962
|
+
return
|
|
963
|
+
}
|
|
964
|
+
let enabled = LlamaNativeBridge.isMultimodalEnabled(contextId: nativeId)
|
|
965
|
+
context.isMultimodalEnabled = enabled
|
|
966
|
+
completion(.success(enabled))
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
func getMultimodalSupport(contextId: Int, completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
970
|
+
guard contexts[contextId] != nil else {
|
|
971
|
+
completion(.failure(.contextNotFound))
|
|
972
|
+
return
|
|
973
|
+
}
|
|
974
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
975
|
+
completion(.success(["vision": false, "audio": false]))
|
|
976
|
+
return
|
|
977
|
+
}
|
|
978
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
979
|
+
do {
|
|
980
|
+
let support = try LlamaNativeBridge.getMultimodalSupport(contextId: nativeId)
|
|
981
|
+
completion(.success(support))
|
|
982
|
+
} catch {
|
|
983
|
+
completion(.success(["vision": false, "audio": false]))
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
func releaseMultimodal(contextId: Int, completion: @escaping (LlamaResult<Void>) -> Void) {
|
|
989
|
+
guard let context = contexts[contextId] else {
|
|
990
|
+
completion(.failure(.contextNotFound))
|
|
991
|
+
return
|
|
992
|
+
}
|
|
993
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
994
|
+
context.isMultimodalEnabled = false
|
|
995
|
+
completion(.success(()))
|
|
996
|
+
return
|
|
997
|
+
}
|
|
998
|
+
LlamaNativeBridge.releaseMultimodal(contextId: nativeId)
|
|
999
|
+
context.isMultimodalEnabled = false
|
|
1000
|
+
completion(.success(()))
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// MARK: - TTS methods
|
|
1004
|
+
|
|
1005
|
+
func initVocoder(contextId: Int, path: String, nBatch: Int?, completion: @escaping (LlamaResult<Bool>) -> Void) {
|
|
1006
|
+
guard let context = contexts[contextId] else {
|
|
1007
|
+
completion(.failure(.contextNotFound))
|
|
1008
|
+
return
|
|
1009
|
+
}
|
|
1010
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
1011
|
+
completion(.failure(.contextNotFound))
|
|
1012
|
+
return
|
|
1013
|
+
}
|
|
1014
|
+
let batchSize = nBatch ?? 512
|
|
1015
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
1016
|
+
do {
|
|
1017
|
+
let ok = try LlamaNativeBridge.initVocoder(contextId: nativeId, path: path, nBatch: batchSize)
|
|
1018
|
+
if ok { context.isVocoderEnabled = true }
|
|
1019
|
+
completion(.success(ok))
|
|
1020
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
1021
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
1022
|
+
} catch {
|
|
1023
|
+
completion(.failure(.operationFailed("initVocoder failed: \(error.localizedDescription)")))
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
func isVocoderEnabled(contextId: Int, completion: @escaping (LlamaResult<Bool>) -> Void) {
|
|
1029
|
+
guard let context = contexts[contextId] else {
|
|
1030
|
+
completion(.failure(.contextNotFound))
|
|
1031
|
+
return
|
|
1032
|
+
}
|
|
1033
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
1034
|
+
completion(.success(context.isVocoderEnabled))
|
|
1035
|
+
return
|
|
1036
|
+
}
|
|
1037
|
+
let enabled = LlamaNativeBridge.isVocoderEnabled(contextId: nativeId)
|
|
1038
|
+
context.isVocoderEnabled = enabled
|
|
1039
|
+
completion(.success(enabled))
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
func getFormattedAudioCompletion(contextId: Int, speakerJsonStr: String, textToSpeak: String, completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
1043
|
+
guard contexts[contextId] != nil else {
|
|
1044
|
+
completion(.failure(.contextNotFound))
|
|
1045
|
+
return
|
|
1046
|
+
}
|
|
1047
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
1048
|
+
completion(.failure(.contextNotFound))
|
|
1049
|
+
return
|
|
1050
|
+
}
|
|
1051
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
1052
|
+
do {
|
|
1053
|
+
let result = try LlamaNativeBridge.getFormattedAudioCompletion(
|
|
1054
|
+
contextId: nativeId, speakerJson: speakerJsonStr, text: textToSpeak)
|
|
1055
|
+
completion(.success(result))
|
|
1056
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
1057
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
1058
|
+
} catch {
|
|
1059
|
+
completion(.failure(.operationFailed("getFormattedAudioCompletion failed: \(error.localizedDescription)")))
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
func getAudioCompletionGuideTokens(contextId: Int, textToSpeak: String, completion: @escaping (LlamaResult<[Int]>) -> Void) {
|
|
1065
|
+
guard contexts[contextId] != nil else {
|
|
1066
|
+
completion(.failure(.contextNotFound))
|
|
1067
|
+
return
|
|
1068
|
+
}
|
|
1069
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
1070
|
+
completion(.failure(.contextNotFound))
|
|
1071
|
+
return
|
|
1072
|
+
}
|
|
1073
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
1074
|
+
do {
|
|
1075
|
+
let tokens = try LlamaNativeBridge.getAudioCompletionGuideTokens(contextId: nativeId, text: textToSpeak)
|
|
1076
|
+
completion(.success(tokens))
|
|
1077
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
1078
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
1079
|
+
} catch {
|
|
1080
|
+
completion(.failure(.operationFailed("getAudioCompletionGuideTokens failed: \(error.localizedDescription)")))
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
func decodeAudioTokens(contextId: Int, tokens: [Int], completion: @escaping (LlamaResult<[Int]>) -> Void) {
|
|
1086
|
+
guard contexts[contextId] != nil else {
|
|
1087
|
+
completion(.failure(.contextNotFound))
|
|
1088
|
+
return
|
|
1089
|
+
}
|
|
1090
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
1091
|
+
completion(.failure(.contextNotFound))
|
|
1092
|
+
return
|
|
1093
|
+
}
|
|
1094
|
+
do {
|
|
1095
|
+
let data = try JSONSerialization.data(withJSONObject: tokens)
|
|
1096
|
+
let json = String(data: data, encoding: .utf8) ?? "[]"
|
|
1097
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
1098
|
+
do {
|
|
1099
|
+
let floatSamples = try LlamaNativeBridge.decodeAudioTokens(contextId: nativeId, tokensJson: json)
|
|
1100
|
+
// Convert float PCM [-1,1] to Int16 range for interop
|
|
1101
|
+
let intSamples = floatSamples.map { Int(($0 * 32767.0).clamped(to: -32768...32767)) }
|
|
1102
|
+
completion(.success(intSamples))
|
|
1103
|
+
} catch let err as LlamaNativeBridge.Failure {
|
|
1104
|
+
completion(.failure(.operationFailed(err.localizedDescription)))
|
|
1105
|
+
} catch {
|
|
1106
|
+
completion(.failure(.operationFailed("decodeAudioTokens failed: \(error.localizedDescription)")))
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
} catch {
|
|
1110
|
+
completion(.failure(.operationFailed("Failed to serialize tokens: \(error.localizedDescription)")))
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
func releaseVocoder(contextId: Int, completion: @escaping (LlamaResult<Void>) -> Void) {
|
|
1115
|
+
guard let context = contexts[contextId] else {
|
|
1116
|
+
completion(.failure(.contextNotFound))
|
|
1117
|
+
return
|
|
1118
|
+
}
|
|
1119
|
+
guard let nativeId = contextIdToNative[contextId] else {
|
|
1120
|
+
context.isVocoderEnabled = false
|
|
1121
|
+
completion(.success(()))
|
|
1122
|
+
return
|
|
1123
|
+
}
|
|
1124
|
+
LlamaNativeBridge.releaseVocoder(contextId: nativeId)
|
|
1125
|
+
context.isVocoderEnabled = false
|
|
1126
|
+
completion(.success(()))
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// MARK: - Model download and management
|
|
1130
|
+
//
|
|
1131
|
+
// Active downloads keyed by URL string. URLSession-based to support real progress
|
|
1132
|
+
// and cancellation — replaces the old blocking Data(contentsOf:) implementation.
|
|
1133
|
+
|
|
1134
|
+
private var activeDownloads: [String: ActiveDownload] = [:]
|
|
1135
|
+
|
|
1136
|
+
private class ActiveDownload: NSObject, URLSessionDownloadDelegate {
|
|
1137
|
+
let url: String
|
|
1138
|
+
let localPath: String
|
|
1139
|
+
var downloadedBytes: Int64 = 0
|
|
1140
|
+
var totalBytes: Int64 = 0
|
|
1141
|
+
var completed: Bool = false
|
|
1142
|
+
var failed: Bool = false
|
|
1143
|
+
var errorMessage: String?
|
|
1144
|
+
var session: URLSession?
|
|
1145
|
+
var task: URLSessionDownloadTask?
|
|
1146
|
+
var onProgress: ((Int64, Int64) -> Void)?
|
|
1147
|
+
var onDone: ((String?) -> Void)?
|
|
1148
|
+
|
|
1149
|
+
init(url: String, localPath: String) {
|
|
1150
|
+
self.url = url
|
|
1151
|
+
self.localPath = localPath
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
|
|
1155
|
+
didWriteData bytesWritten: Int64,
|
|
1156
|
+
totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
|
|
1157
|
+
downloadedBytes = totalBytesWritten
|
|
1158
|
+
totalBytes = totalBytesExpectedToWrite
|
|
1159
|
+
onProgress?(totalBytesWritten, totalBytesExpectedToWrite)
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
|
|
1163
|
+
didFinishDownloadingTo location: URL) {
|
|
1164
|
+
do {
|
|
1165
|
+
let dest = URL(fileURLWithPath: localPath)
|
|
1166
|
+
if FileManager.default.fileExists(atPath: localPath) {
|
|
1167
|
+
try FileManager.default.removeItem(at: dest)
|
|
1168
|
+
}
|
|
1169
|
+
try FileManager.default.moveItem(at: location, to: dest)
|
|
1170
|
+
completed = true
|
|
1171
|
+
onDone?(nil)
|
|
1172
|
+
} catch {
|
|
1173
|
+
failed = true
|
|
1174
|
+
errorMessage = error.localizedDescription
|
|
1175
|
+
onDone?(error.localizedDescription)
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
|
1180
|
+
if let error = error, !completed {
|
|
1181
|
+
failed = true
|
|
1182
|
+
errorMessage = error.localizedDescription
|
|
1183
|
+
onDone?(error.localizedDescription)
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
func downloadModel(url: String, filename: String, completion: @escaping (LlamaResult<String>) -> Void) {
|
|
1189
|
+
guard let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
|
|
1190
|
+
completion(.failure(.operationFailed("Cannot access documents directory")))
|
|
1191
|
+
return
|
|
1192
|
+
}
|
|
1193
|
+
let localPath = documentsDir.appendingPathComponent(filename).path
|
|
1194
|
+
// Return cached file immediately
|
|
1195
|
+
if FileManager.default.fileExists(atPath: localPath) {
|
|
1196
|
+
completion(.success(localPath))
|
|
1197
|
+
return
|
|
1198
|
+
}
|
|
1199
|
+
guard let downloadURL = URL(string: url) else {
|
|
1200
|
+
completion(.failure(.operationFailed("Invalid URL: \(url)")))
|
|
1201
|
+
return
|
|
1202
|
+
}
|
|
1203
|
+
let dl = ActiveDownload(url: url, localPath: localPath)
|
|
1204
|
+
let config = URLSessionConfiguration.default
|
|
1205
|
+
let session = URLSession(configuration: config, delegate: dl, delegateQueue: nil)
|
|
1206
|
+
dl.session = session
|
|
1207
|
+
dl.onDone = { [weak self] errorMsg in
|
|
1208
|
+
self?.activeDownloads.removeValue(forKey: url)
|
|
1209
|
+
if let msg = errorMsg {
|
|
1210
|
+
// Don't call completion again; the path was already returned
|
|
1211
|
+
print("[LlamaCpp] Download failed for \(url): \(msg)")
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
activeDownloads[url] = dl
|
|
1215
|
+
let task = session.downloadTask(with: downloadURL)
|
|
1216
|
+
dl.task = task
|
|
1217
|
+
task.resume()
|
|
1218
|
+
// Return the path immediately; caller polls getDownloadProgress
|
|
1219
|
+
completion(.success(localPath))
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
func getDownloadProgress(url: String, completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
1223
|
+
guard let dl = activeDownloads[url] else {
|
|
1224
|
+
// Check if the file exists (completed in a previous session)
|
|
1225
|
+
let result: [String: Any] = [
|
|
1226
|
+
"progress": 0, "completed": false, "failed": false,
|
|
1227
|
+
"downloadedBytes": 0, "totalBytes": 0
|
|
1228
|
+
]
|
|
1229
|
+
completion(.success(result))
|
|
1230
|
+
return
|
|
1231
|
+
}
|
|
1232
|
+
let progress: Double = dl.totalBytes > 0 ? Double(dl.downloadedBytes) / Double(dl.totalBytes) : 0
|
|
1233
|
+
let result: [String: Any] = [
|
|
1234
|
+
"progress": progress,
|
|
1235
|
+
"completed": dl.completed,
|
|
1236
|
+
"failed": dl.failed,
|
|
1237
|
+
"errorMessage": dl.errorMessage ?? "",
|
|
1238
|
+
"localPath": dl.localPath,
|
|
1239
|
+
"downloadedBytes": dl.downloadedBytes,
|
|
1240
|
+
"totalBytes": dl.totalBytes
|
|
1241
|
+
]
|
|
1242
|
+
completion(.success(result))
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
func cancelDownload(url: String, completion: @escaping (LlamaResult<Bool>) -> Void) {
|
|
1246
|
+
guard let dl = activeDownloads[url] else {
|
|
1247
|
+
completion(.success(false))
|
|
1248
|
+
return
|
|
1249
|
+
}
|
|
1250
|
+
dl.task?.cancel()
|
|
1251
|
+
dl.session?.invalidateAndCancel()
|
|
1252
|
+
activeDownloads.removeValue(forKey: url)
|
|
1253
|
+
completion(.success(true))
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
func getAvailableModels(completion: @escaping (LlamaResult<[[String: Any]]>) -> Void) {
|
|
1257
|
+
let fileManager = FileManager.default
|
|
1258
|
+
var models: [[String: Any]] = []
|
|
1259
|
+
|
|
1260
|
+
// Search common model directories
|
|
1261
|
+
let searchPaths = [
|
|
1262
|
+
fileManager.urls(for: .documentDirectory, in: .userDomainMask).first,
|
|
1263
|
+
fileManager.urls(for: .downloadsDirectory, in: .userDomainMask).first
|
|
1264
|
+
].compactMap { $0 }
|
|
1265
|
+
|
|
1266
|
+
for searchPath in searchPaths {
|
|
1267
|
+
do {
|
|
1268
|
+
let files = try fileManager.contentsOfDirectory(at: searchPath, includingPropertiesForKeys: [.fileSizeKey, .contentModificationDateKey])
|
|
1269
|
+
|
|
1270
|
+
for file in files {
|
|
1271
|
+
let pathExtension = file.pathExtension.lowercased()
|
|
1272
|
+
// Check for common model file extensions
|
|
1273
|
+
if pathExtension == "gguf" || pathExtension == "ggml" || pathExtension == "bin" {
|
|
1274
|
+
let attributes = try fileManager.attributesOfItem(atPath: file.path)
|
|
1275
|
+
let fileSize = attributes[.size] as? Int64 ?? 0
|
|
1276
|
+
|
|
1277
|
+
let model: [String: Any] = [
|
|
1278
|
+
"id": file.lastPathComponent,
|
|
1279
|
+
"name": file.lastPathComponent,
|
|
1280
|
+
"path": file.path,
|
|
1281
|
+
"size": fileSize,
|
|
1282
|
+
"sizeMB": Double(fileSize) / (1024 * 1024),
|
|
1283
|
+
"status": "available"
|
|
1284
|
+
]
|
|
1285
|
+
models.append(model)
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
} catch {
|
|
1289
|
+
// Continue searching other paths
|
|
1290
|
+
continue
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
completion(.success(models))
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// MARK: - Native in-process HTTP server (127.0.0.1 — use App Transport Security allowances for localhost in the host app)
|
|
1298
|
+
|
|
1299
|
+
func startNativeLlamaServer(modelPath: String, host: String?, port: Int, params: [String: Any], completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
1300
|
+
if capServerStartFunc == nil { loadFunctionPointers() }
|
|
1301
|
+
guard let start = capServerStartFunc else {
|
|
1302
|
+
completion(.failure(.operationFailed("cap_llama_server_start not available")))
|
|
1303
|
+
return
|
|
1304
|
+
}
|
|
1305
|
+
var paramsJson = "{}"
|
|
1306
|
+
do {
|
|
1307
|
+
let data = try JSONSerialization.data(withJSONObject: params)
|
|
1308
|
+
paramsJson = String(data: data, encoding: .utf8) ?? "{}"
|
|
1309
|
+
} catch {
|
|
1310
|
+
completion(.failure(.operationFailed("params JSON: \(error.localizedDescription)")))
|
|
1311
|
+
return
|
|
1312
|
+
}
|
|
1313
|
+
let h = (host != nil && !host!.isEmpty) ? host! : "127.0.0.1"
|
|
1314
|
+
let rc = modelPath.withCString { mp in
|
|
1315
|
+
h.withCString { hp in
|
|
1316
|
+
paramsJson.withCString { pj in
|
|
1317
|
+
start(mp, hp, Int32(port), pj)
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
if rc != 0 {
|
|
1322
|
+
completion(.success(["running": true]))
|
|
1323
|
+
} else {
|
|
1324
|
+
completion(.failure(.operationFailed("startNativeLlamaServer failed")))
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
func stopNativeLlamaServer(completion: @escaping (LlamaResult<Void>) -> Void) {
|
|
1329
|
+
if capServerStopFunc == nil { loadFunctionPointers() }
|
|
1330
|
+
guard let stop = capServerStopFunc else {
|
|
1331
|
+
completion(.failure(.operationFailed("cap_llama_server_stop not available")))
|
|
1332
|
+
return
|
|
1333
|
+
}
|
|
1334
|
+
stop()
|
|
1335
|
+
completion(.success(()))
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
func isNativeLlamaServerRunning(completion: @escaping (LlamaResult<[String: Any]>) -> Void) {
|
|
1339
|
+
if capServerIsRunningFunc == nil { loadFunctionPointers() }
|
|
1340
|
+
guard let fn = capServerIsRunningFunc else {
|
|
1341
|
+
completion(.failure(.operationFailed("cap_llama_server_is_running not available")))
|
|
1342
|
+
return
|
|
1343
|
+
}
|
|
1344
|
+
let running = fn() != 0
|
|
1345
|
+
completion(.success(["running": running]))
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// MARK: - Grammar utilities
|
|
1349
|
+
|
|
1350
|
+
func convertJsonSchemaToGrammar(schema: String, completion: @escaping (LlamaResult<String>) -> Void) {
|
|
1351
|
+
if grammarFunc == nil { loadFunctionPointers() }
|
|
1352
|
+
guard let fn = grammarFunc else {
|
|
1353
|
+
completion(.success(schema))
|
|
1354
|
+
return
|
|
1355
|
+
}
|
|
1356
|
+
let s: LlamaResult<String> = schema.withCString { ptr in
|
|
1357
|
+
guard let c = fn(ptr) else {
|
|
1358
|
+
return .success("")
|
|
1359
|
+
}
|
|
1360
|
+
let g = String(cString: c)
|
|
1361
|
+
return g.isEmpty ? .success(schema) : .success(g)
|
|
1362
|
+
}
|
|
1363
|
+
completion(s)
|
|
1364
|
+
}
|
|
1365
|
+
}
|